<!DOCTYPE html>
<html lang="zh-CN" dir="ltr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>14.8 绘制（四）Compositing | 落光的Pro博客</title>
    <meta name="description" content="Vite & Vue powered static site generator.">
    <link rel="preload stylesheet" href="./assets/style.4b0df91d.css" as="style">
    
    <script type="module" src="./assets/app.0acbd3fd.js"></script>
    <link rel="preload" href="./assets/inter-roman-latin.2ed14f66.woff2" as="font" type="font/woff2" crossorigin="">
    <link rel="modulepreload" href="./assets/chunks/framework.eaf25f5b.js">
    <link rel="modulepreload" href="./assets/chunks/theme.ce44a0e6.js">
    <link rel="modulepreload" href="./assets/largeFrontEnd_flutter_chapter14_compositing.md.6d96f932.lean.js">
    <script id="check-dark-light">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
  </head>
  <body>
    <div id="app"><div class="Layout" data-v-1919c326><!--[--><!--]--><!--[--><span tabindex="-1" data-v-0f60ec36></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-0f60ec36> Skip to content </a><!--]--><!----><header class="VPNav" data-v-1919c326 data-v-7e5bc4a5><div class="VPNavBar has-sidebar" data-v-7e5bc4a5 data-v-a0fd61f4><div class="container" data-v-a0fd61f4><div class="title" data-v-a0fd61f4><div class="VPNavBarTitle has-sidebar" data-v-a0fd61f4 data-v-86d1bed8><a class="title" href="./" data-v-86d1bed8><!--[--><!--]--><!--[--><img class="VPImage logo" src="./icon-radius.png" alt data-v-8426fc1a><!--]--><!--[-->落光的Pro博客<!--]--><!--[--><!--]--></a></div></div><div class="content" data-v-a0fd61f4><div class="curtain" data-v-a0fd61f4></div><div class="content-body" data-v-a0fd61f4><!--[--><!--]--><div class="VPNavBarSearch search" style="--vp-meta-key:&#39;Meta&#39;;" data-v-a0fd61f4><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg class="DocSearch-Search-Icon" width="20" height="20" viewBox="0 0 20 20" aria-label="search icon"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-a0fd61f4 data-v-7f418b0f><span id="main-nav-aria-label" class="visually-hidden" data-v-7f418b0f>Main Navigation</span><!--[--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-9c007e85><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-9c007e85><span class="text" data-v-9c007e85><!----><span data-v-9c007e85>大前端</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-9c007e85><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-9c007e85><div class="VPMenu" data-v-9c007e85 data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/webFrontEnd/md/01.html" data-v-43f1e123><!--[-->Web前端<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/uniapp/" data-v-43f1e123><!--[-->Uni-app<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/weChatMiniProgram/" data-v-43f1e123><!--[-->微信小程序<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/electron/" data-v-43f1e123><!--[-->Electron<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/flutter/" data-v-43f1e123><!--[-->Flutter<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/interview/01.html" data-v-43f1e123><!--[-->前端面试题<!--]--></a></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="./backEnd/" tabindex="0" data-v-7f418b0f data-v-42ef59de><!--[--><span data-v-42ef59de>后端</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="./linux/" tabindex="0" data-v-7f418b0f data-v-42ef59de><!--[--><span data-v-42ef59de>Linux</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="./myTeam/" tabindex="0" data-v-7f418b0f data-v-42ef59de><!--[--><span data-v-42ef59de>我的团队</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-a0fd61f4 data-v-f6a63727><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="toggle dark mode" aria-checked="false" data-v-f6a63727 data-v-ce54a7d1 data-v-b1685198><span class="check" data-v-b1685198><span class="icon" data-v-b1685198><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-ce54a7d1><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-ce54a7d1><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-a0fd61f4 data-v-0394ad82 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://gitee.com/luoguangguang" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><a class="VPSocialLink no-icon" href="..." aria-label="cool link" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Dribbble</title><path d="M12...6.38z"/></svg></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-a0fd61f4 data-v-40855f84 data-v-9c007e85><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-9c007e85><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="icon" data-v-9c007e85><circle cx="12" cy="12" r="2"></circle><circle cx="19" cy="12" r="2"></circle><circle cx="5" cy="12" r="2"></circle></svg></button><div class="menu" data-v-9c007e85><div class="VPMenu" data-v-9c007e85 data-v-e7ea1737><!----><!--[--><!--[--><!----><div class="group" data-v-40855f84><div class="item appearance" data-v-40855f84><p class="label" data-v-40855f84>Appearance</p><div class="appearance-action" data-v-40855f84><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="toggle dark mode" aria-checked="false" data-v-40855f84 data-v-ce54a7d1 data-v-b1685198><span class="check" data-v-b1685198><span class="icon" data-v-b1685198><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-ce54a7d1><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-ce54a7d1><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div></div></div><div class="group" data-v-40855f84><div class="item social-links" data-v-40855f84><div class="VPSocialLinks social-links-list" data-v-40855f84 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://gitee.com/luoguangguang" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><a class="VPSocialLink no-icon" href="..." aria-label="cool link" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Dribbble</title><path d="M12...6.38z"/></svg></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-a0fd61f4 data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><!----></header><div class="VPLocalNav reached-top" data-v-1919c326 data-v-79c8c1df><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-79c8c1df><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="menu-icon" data-v-79c8c1df><path d="M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"></path><path d="M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"></path><path d="M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"></path><path d="M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"></path></svg><span class="menu-text" data-v-79c8c1df>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-79c8c1df data-v-1c15a60a><button data-v-1c15a60a>Return to top</button><!----></div></div><aside class="VPSidebar" data-v-1919c326 data-v-b00e2fdd><div class="curtain" data-v-b00e2fdd></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-b00e2fdd><span class="visually-hidden" id="sidebar-aria-label" data-v-b00e2fdd> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="group" data-v-b00e2fdd><section class="VPSidebarItem level-0 collapsible" data-v-b00e2fdd data-v-e31bd47b><div class="item" role="button" tabindex="0" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><h2 class="text" data-v-e31bd47b>Flutter入门到实战</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-e31bd47b><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-e31bd47b><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-e31bd47b><!--[--><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter1/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第一章 初识Flutter</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter2/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第二章 简介</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter3/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第三章 基础组件</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter4/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第四章 布局类组件</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter5/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第五章 容器类Widget</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter6/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第六章 可滚动组件</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter7/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第七章 功能型Widget简介</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter8/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第八章 事件处理与通知</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter9/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第九章 动画</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter10/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第十章 自定义组件</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter11/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第十一章 文件操作</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter12/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第十二章 Flutter 扩展</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter13/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第十三章 多语言</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter14/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第十四章 高级进阶</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-1919c326 data-v-669faec9><div class="VPDoc has-sidebar has-aside" data-v-669faec9 data-v-6b87e69f><!--[--><!--]--><div class="container" data-v-6b87e69f><div class="aside" data-v-6b87e69f><div class="aside-curtain" data-v-6b87e69f></div><div class="aside-container" data-v-6b87e69f><div class="aside-content" data-v-6b87e69f><div class="VPDocAside" data-v-6b87e69f data-v-3f215769><!--[--><!--]--><!--[--><!--]--><div class="VPDocAsideOutline" role="navigation" data-v-3f215769 data-v-6ae8e080><div class="content" data-v-6ae8e080><div class="outline-marker" data-v-6ae8e080></div><div class="outline-title" role="heading" data-v-6ae8e080>On this page</div><nav aria-labelledby="doc-outline-aria-label" data-v-6ae8e080><span class="visually-hidden" id="doc-outline-aria-label" data-v-6ae8e080> Table of Contents for current page </span><ul class="root" data-v-6ae8e080 data-v-d0ee3533><!--[--><!--]--></ul></nav></div></div><!--[--><!--]--><div class="spacer" data-v-3f215769></div><!--[--><!--]--><div class="VPDocAsideCarbonAds" data-v-3f215769><div class="VPCarbonAds" data-v-2e1efd59></div></div><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-6b87e69f><div class="content-container" data-v-6b87e69f><!--[--><!--]--><!----><main class="main" data-v-6b87e69f><div style="position:relative;" class="vp-doc _largeFrontEnd_flutter_chapter14_compositing" data-v-6b87e69f><div><h1 id="_14-8-绘制-四-compositing" tabindex="-1">14.8 绘制（四）Compositing <a class="header-anchor" href="#_14-8-绘制-四-compositing" aria-label="Permalink to &quot;14.8 绘制（四）Compositing&quot;">​</a></h1><p>本节我们来介绍一下 flushCompositingBits()。现在，我们再来回顾一下Flutter的渲染管线：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">drawFrame</span><span style="color:#E1E4E8;">(){</span></span>
<span class="line"><span style="color:#E1E4E8;">  pipelineOwner.</span><span style="color:#B392F0;">flushLayout</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  pipelineOwner.</span><span style="color:#B392F0;">flushCompositingBits</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  pipelineOwner.</span><span style="color:#B392F0;">flushPaint</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  renderView.</span><span style="color:#B392F0;">compositeFrame</span><span style="color:#E1E4E8;">()</span></span>
<span class="line"><span style="color:#E1E4E8;">  ...</span><span style="color:#6A737D;">//省略  </span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">drawFrame</span><span style="color:#24292E;">(){</span></span>
<span class="line"><span style="color:#24292E;">  pipelineOwner.</span><span style="color:#6F42C1;">flushLayout</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  pipelineOwner.</span><span style="color:#6F42C1;">flushCompositingBits</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  pipelineOwner.</span><span style="color:#6F42C1;">flushPaint</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  renderView.</span><span style="color:#6F42C1;">compositeFrame</span><span style="color:#24292E;">()</span></span>
<span class="line"><span style="color:#24292E;">  ...</span><span style="color:#6A737D;">//省略  </span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>其中只有 flushCompositingBits() 还没有介绍过，这是因为要理解flushCompositingBits()，就必须的了解Layer是什么，以及 Layer 树构建的过程。为了更容易理解它，我们先看一个demo。</p><h2 id="_14-8-1-customrotatedbox" tabindex="-1">14.8.1 CustomRotatedBox <a class="header-anchor" href="#_14-8-1-customrotatedbox" aria-label="Permalink to &quot;14.8.1 CustomRotatedBox&quot;">​</a></h2><p>我们实现一个CustomRotatedBox，它的功能是将其子元素放倒（顺时针旋转 90 度），要实现个效果我们可以直接使用 canvas 的变换功能，下面是核心代码：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">class</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">CustomRotatedBox</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">extends</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">SingleChildRenderObjectWidget</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">CustomRotatedBox</span><span style="color:#E1E4E8;">({</span><span style="color:#79B8FF;">Key</span><span style="color:#F97583;">?</span><span style="color:#E1E4E8;"> key, </span><span style="color:#79B8FF;">Widget</span><span style="color:#F97583;">?</span><span style="color:#E1E4E8;"> child}) </span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">super</span><span style="color:#E1E4E8;">(key</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> key, child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> child);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">RenderObject</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">createRenderObject</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">BuildContext</span><span style="color:#E1E4E8;"> context) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">CustomRenderRotatedBox</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583;">class</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">CustomRenderRotatedBox</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">extends</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">RenderBox</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">with</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">RenderObjectWithChildMixin</span><span style="color:#E1E4E8;">&lt;</span><span style="color:#79B8FF;">RenderBox</span><span style="color:#E1E4E8;">&gt; {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">performLayout</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">    _paintTransform </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">      child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">layout</span><span style="color:#E1E4E8;">(constraints, parentUsesSize</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">true</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">      size </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.size;</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#6A737D;">//根据子组件大小计算出旋转矩阵</span></span>
<span class="line"><span style="color:#E1E4E8;">      _paintTransform </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">identity</span><span style="color:#E1E4E8;">()</span></span>
<span class="line"><span style="color:#E1E4E8;">        ..</span><span style="color:#B392F0;">translate</span><span style="color:#E1E4E8;">(size.width </span><span style="color:#F97583;">/</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">2.0</span><span style="color:#E1E4E8;">, size.height </span><span style="color:#F97583;">/</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">2.0</span><span style="color:#E1E4E8;">)</span></span>
<span class="line"><span style="color:#E1E4E8;">        ..</span><span style="color:#B392F0;">rotateZ</span><span style="color:#E1E4E8;">(math.pi </span><span style="color:#F97583;">/</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">2</span><span style="color:#E1E4E8;">) </span><span style="color:#6A737D;">// 旋转90度</span></span>
<span class="line"><span style="color:#E1E4E8;">        ..</span><span style="color:#B392F0;">translate</span><span style="color:#E1E4E8;">(</span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;">child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.size.width </span><span style="color:#F97583;">/</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">2.0</span><span style="color:#E1E4E8;">, </span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;">child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.size.height </span><span style="color:#F97583;">/</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">2.0</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">    } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">      size </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> constraints.smallest;</span></span>
<span class="line"><span style="color:#E1E4E8;">    }</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">paint</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;">(child</span><span style="color:#F97583;">!=</span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">){</span></span>
<span class="line"><span style="color:#E1E4E8;">       </span><span style="color:#6A737D;">// 根据偏移，需要调整一下旋转矩阵</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#F97583;">final</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;"> transform </span><span style="color:#F97583;">=</span></span>
<span class="line"><span style="color:#E1E4E8;">          </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">translationValues</span><span style="color:#E1E4E8;">(offset.dx, offset.dy, </span><span style="color:#79B8FF;">0.0</span><span style="color:#E1E4E8;">)</span></span>
<span class="line"><span style="color:#E1E4E8;">            ..</span><span style="color:#B392F0;">multiply</span><span style="color:#E1E4E8;">(_paintTransform</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">)</span></span>
<span class="line"><span style="color:#E1E4E8;">            ..</span><span style="color:#B392F0;">translate</span><span style="color:#E1E4E8;">(</span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;">offset.dx, </span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;">offset.dy);</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#B392F0;">_paint</span><span style="color:#E1E4E8;">(context, offset, transform);</span></span>
<span class="line"><span style="color:#E1E4E8;">    } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#6A737D;">//...</span></span>
<span class="line"><span style="color:#E1E4E8;">    }</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span></span>
<span class="line"><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">_paint</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context,</span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset,</span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;"> transform ){</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#6A737D;">// 为了不干扰其他和自己在同一个layer上绘制的节点，所以需要先调用save然后在子元素绘制完后</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#6A737D;">// 再调用restore显示，关于save/restore有兴趣可以查看Canvas API doc</span></span>
<span class="line"><span style="color:#E1E4E8;">    context.canvas</span></span>
<span class="line"><span style="color:#E1E4E8;">      ..</span><span style="color:#B392F0;">save</span><span style="color:#E1E4E8;">()</span></span>
<span class="line"><span style="color:#E1E4E8;">      ..</span><span style="color:#B392F0;">transform</span><span style="color:#E1E4E8;">(transform.storage);</span></span>
<span class="line"><span style="color:#E1E4E8;">    context.</span><span style="color:#B392F0;">paintChild</span><span style="color:#E1E4E8;">(child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">, offset);</span></span>
<span class="line"><span style="color:#E1E4E8;">    context.canvas.</span><span style="color:#B392F0;">restore</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">  ... </span><span style="color:#6A737D;">//省略无关代码</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">class</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">CustomRotatedBox</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">extends</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">SingleChildRenderObjectWidget</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">CustomRotatedBox</span><span style="color:#24292E;">({</span><span style="color:#005CC5;">Key</span><span style="color:#D73A49;">?</span><span style="color:#24292E;"> key, </span><span style="color:#005CC5;">Widget</span><span style="color:#D73A49;">?</span><span style="color:#24292E;"> child}) </span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">super</span><span style="color:#24292E;">(key</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> key, child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> child);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">RenderObject</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">createRenderObject</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">BuildContext</span><span style="color:#24292E;"> context) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">CustomRenderRotatedBox</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;">class</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">CustomRenderRotatedBox</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">extends</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">RenderBox</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">with</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">RenderObjectWithChildMixin</span><span style="color:#24292E;">&lt;</span><span style="color:#005CC5;">RenderBox</span><span style="color:#24292E;">&gt; {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">performLayout</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">    _paintTransform </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">      child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">layout</span><span style="color:#24292E;">(constraints, parentUsesSize</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">true</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">      size </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.size;</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6A737D;">//根据子组件大小计算出旋转矩阵</span></span>
<span class="line"><span style="color:#24292E;">      _paintTransform </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">identity</span><span style="color:#24292E;">()</span></span>
<span class="line"><span style="color:#24292E;">        ..</span><span style="color:#6F42C1;">translate</span><span style="color:#24292E;">(size.width </span><span style="color:#D73A49;">/</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">2.0</span><span style="color:#24292E;">, size.height </span><span style="color:#D73A49;">/</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">2.0</span><span style="color:#24292E;">)</span></span>
<span class="line"><span style="color:#24292E;">        ..</span><span style="color:#6F42C1;">rotateZ</span><span style="color:#24292E;">(math.pi </span><span style="color:#D73A49;">/</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">2</span><span style="color:#24292E;">) </span><span style="color:#6A737D;">// 旋转90度</span></span>
<span class="line"><span style="color:#24292E;">        ..</span><span style="color:#6F42C1;">translate</span><span style="color:#24292E;">(</span><span style="color:#D73A49;">-</span><span style="color:#24292E;">child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.size.width </span><span style="color:#D73A49;">/</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">2.0</span><span style="color:#24292E;">, </span><span style="color:#D73A49;">-</span><span style="color:#24292E;">child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.size.height </span><span style="color:#D73A49;">/</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">2.0</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">    } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">      size </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> constraints.smallest;</span></span>
<span class="line"><span style="color:#24292E;">    }</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">paint</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;">(child</span><span style="color:#D73A49;">!=</span><span style="color:#005CC5;">null</span><span style="color:#24292E;">){</span></span>
<span class="line"><span style="color:#24292E;">       </span><span style="color:#6A737D;">// 根据偏移，需要调整一下旋转矩阵</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#D73A49;">final</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;"> transform </span><span style="color:#D73A49;">=</span></span>
<span class="line"><span style="color:#24292E;">          </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">translationValues</span><span style="color:#24292E;">(offset.dx, offset.dy, </span><span style="color:#005CC5;">0.0</span><span style="color:#24292E;">)</span></span>
<span class="line"><span style="color:#24292E;">            ..</span><span style="color:#6F42C1;">multiply</span><span style="color:#24292E;">(_paintTransform</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">)</span></span>
<span class="line"><span style="color:#24292E;">            ..</span><span style="color:#6F42C1;">translate</span><span style="color:#24292E;">(</span><span style="color:#D73A49;">-</span><span style="color:#24292E;">offset.dx, </span><span style="color:#D73A49;">-</span><span style="color:#24292E;">offset.dy);</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6F42C1;">_paint</span><span style="color:#24292E;">(context, offset, transform);</span></span>
<span class="line"><span style="color:#24292E;">    } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6A737D;">//...</span></span>
<span class="line"><span style="color:#24292E;">    }</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">  </span></span>
<span class="line"><span style="color:#24292E;"> </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">_paint</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context,</span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset,</span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;"> transform ){</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6A737D;">// 为了不干扰其他和自己在同一个layer上绘制的节点，所以需要先调用save然后在子元素绘制完后</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6A737D;">// 再调用restore显示，关于save/restore有兴趣可以查看Canvas API doc</span></span>
<span class="line"><span style="color:#24292E;">    context.canvas</span></span>
<span class="line"><span style="color:#24292E;">      ..</span><span style="color:#6F42C1;">save</span><span style="color:#24292E;">()</span></span>
<span class="line"><span style="color:#24292E;">      ..</span><span style="color:#6F42C1;">transform</span><span style="color:#24292E;">(transform.storage);</span></span>
<span class="line"><span style="color:#24292E;">    context.</span><span style="color:#6F42C1;">paintChild</span><span style="color:#24292E;">(child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">, offset);</span></span>
<span class="line"><span style="color:#24292E;">    context.canvas.</span><span style="color:#6F42C1;">restore</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">  ... </span><span style="color:#6A737D;">//省略无关代码</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>下面我们写个demo测试一下：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">class</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">CustomRotatedBoxTest</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">extends</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">StatelessWidget</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">const</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">CustomRotatedBoxTest</span><span style="color:#E1E4E8;">({</span><span style="color:#79B8FF;">Key</span><span style="color:#F97583;">?</span><span style="color:#E1E4E8;"> key}) </span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">super</span><span style="color:#E1E4E8;">(key</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> key);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">Widget</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">build</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">BuildContext</span><span style="color:#E1E4E8;"> context) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Center</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">      child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">CustomRotatedBox</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">        child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Text</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">          </span><span style="color:#9ECBFF;">&quot;A&quot;</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">          textScaleFactor</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">5</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">        ),</span></span>
<span class="line"><span style="color:#E1E4E8;">      ),</span></span>
<span class="line"><span style="color:#E1E4E8;">    );</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">class</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">CustomRotatedBoxTest</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">extends</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">StatelessWidget</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">const</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">CustomRotatedBoxTest</span><span style="color:#24292E;">({</span><span style="color:#005CC5;">Key</span><span style="color:#D73A49;">?</span><span style="color:#24292E;"> key}) </span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">super</span><span style="color:#24292E;">(key</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> key);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">Widget</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">build</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">BuildContext</span><span style="color:#24292E;"> context) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Center</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">      child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">CustomRotatedBox</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">        child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Text</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">          </span><span style="color:#032F62;">&quot;A&quot;</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">          textScaleFactor</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">5</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">        ),</span></span>
<span class="line"><span style="color:#24292E;">      ),</span></span>
<span class="line"><span style="color:#24292E;">    );</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>运行效果如图14-17，A被成功放倒了：</p><p><img src="/assets/14-17.db7df290.png" alt="图14-17"></p><p>现在我们给 CustomRotatedBox 添加一个 RepaintBoundary 再试试：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#79B8FF;">Widget</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">build</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">BuildContext</span><span style="color:#E1E4E8;"> context) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Center</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">    child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">CustomRotatedBox</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">      child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">RepaintBoundary</span><span style="color:#E1E4E8;">( </span><span style="color:#6A737D;">// 添加一个 RepaintBoundary</span></span>
<span class="line"><span style="color:#E1E4E8;">        child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Text</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">          </span><span style="color:#9ECBFF;">&quot;A&quot;</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">          textScaleFactor</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">5</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">        ),</span></span>
<span class="line"><span style="color:#E1E4E8;">      ),</span></span>
<span class="line"><span style="color:#E1E4E8;">    ),</span></span>
<span class="line"><span style="color:#E1E4E8;">  );</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#005CC5;">Widget</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">build</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">BuildContext</span><span style="color:#24292E;"> context) {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Center</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">    child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">CustomRotatedBox</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">      child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">RepaintBoundary</span><span style="color:#24292E;">( </span><span style="color:#6A737D;">// 添加一个 RepaintBoundary</span></span>
<span class="line"><span style="color:#24292E;">        child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Text</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">          </span><span style="color:#032F62;">&quot;A&quot;</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">          textScaleFactor</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">5</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">        ),</span></span>
<span class="line"><span style="color:#24292E;">      ),</span></span>
<span class="line"><span style="color:#24292E;">    ),</span></span>
<span class="line"><span style="color:#24292E;">  );</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>运行后如图14-18。咦，A怎么又站起来了！</p><p><img src="/assets/14-18.3f9bb9ab.png" alt="图14-18"></p><p>结合图说明！</p><p>我们来分析一下原因：根据上一节的知识，我们可以很容易画出添加 RepaintBoundary 前和后的 Layer 树结构，如图14-19：</p><p><img src="/assets/14-19.11d24a48.png" alt="图14-19"></p><p>添加 RepaintBoundary 后，CustomRotatedBox 中的持有的还是 OffsetLayer1 ：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">_paint</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context,</span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset,</span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;"> transform ){</span></span>
<span class="line"><span style="color:#E1E4E8;">    context.canvas </span><span style="color:#6A737D;">// 该 canvas 对应的是 PictureLayer1 </span></span>
<span class="line"><span style="color:#E1E4E8;">      ..</span><span style="color:#B392F0;">save</span><span style="color:#E1E4E8;">()</span></span>
<span class="line"><span style="color:#E1E4E8;">      ..</span><span style="color:#B392F0;">transform</span><span style="color:#E1E4E8;">(transform.storage);</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#6A737D;">// 子节点是绘制边界节点，会在新的 OffsetLayer2中的 PictureLayer2 上绘制</span></span>
<span class="line"><span style="color:#E1E4E8;">    context.</span><span style="color:#B392F0;">paintChild</span><span style="color:#E1E4E8;">(child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">, offset); </span></span>
<span class="line"><span style="color:#E1E4E8;">    context.canvas.</span><span style="color:#B392F0;">restore</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">  ... </span><span style="color:#6A737D;">//省略无关代码</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">_paint</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context,</span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset,</span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;"> transform ){</span></span>
<span class="line"><span style="color:#24292E;">    context.canvas </span><span style="color:#6A737D;">// 该 canvas 对应的是 PictureLayer1 </span></span>
<span class="line"><span style="color:#24292E;">      ..</span><span style="color:#6F42C1;">save</span><span style="color:#24292E;">()</span></span>
<span class="line"><span style="color:#24292E;">      ..</span><span style="color:#6F42C1;">transform</span><span style="color:#24292E;">(transform.storage);</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6A737D;">// 子节点是绘制边界节点，会在新的 OffsetLayer2中的 PictureLayer2 上绘制</span></span>
<span class="line"><span style="color:#24292E;">    context.</span><span style="color:#6F42C1;">paintChild</span><span style="color:#24292E;">(child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">, offset); </span></span>
<span class="line"><span style="color:#24292E;">    context.canvas.</span><span style="color:#6F42C1;">restore</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">  ... </span><span style="color:#6A737D;">//省略无关代码</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>很显然，CustomRotatedBox 中进行旋转变换的 canvas 对应的是 PictureLayer1，而 Text(&quot;A&quot;) 的绘制是使用的PictureLayer2 对应的 canvas ，他们属于不同的 Layer。可以发现父子的 PictureLayer &quot;分离了&quot;，所以CustomRotatedBox 也就不会对 Text(&quot;A&quot;) 起作用。那么如何解决这个问题的思路呢？</p><p>我们在前面的小节介绍过，有很多容器类组件都附带变换效果，拥有旋转变换的容器类Layer是 TransformLayer，那么我们就可以在 CustomRotatedBox 中绘制子节点之前：</p><ol><li>创建一个TransformLayer（记为 TransformLayer1） 添加到 Layer树中，接着创建一个新的 PaintingContext 和 TransformLayer1绑定。</li><li>子节点通过这个新的 PaintingContext 去绘制。</li></ol><p>完成上述操作之后，后代节点绘制所在的 PictureLayer 都会是 TransformLayer 的子节点，因此我们可以通过 TransformLayer 对所有子节点整体做变换。图14-20是添加是 TransformLayer1前、后的 Layer 树结构。</p><p><img src="/assets/14-20.6a195916.png" alt="图14-20"></p><p>这其实就是一个重新 <strong>Layer 合成（layer compositing）</strong> 的过程：创建一个新的 ContainerLayer，然后将该ContainerLayer 传递给子节点，这样后代节点的Layer必然属于ContainerLayer ，那么给这个 ContainerLayer 做变换就会对其全部的子孙节点生效。因为 “Layer 合成” 在不同的语境会有不同的指代，为了便于描述，本节中 “layer 合成” 或 “合成 layer” 就特指上述过程。</p><blockquote><p>“Layer 合成” 在不同的语境会有不同的指代，比如 skia 最终渲染时也是将一个个 layer 渲染出来，这个过程也可以认为是多个 layer 上的绘制信息合成为最终的位图信息；另外 canvas 中也有 layer 的概念（canvas.save 方法生成新的layer），对应的将所有layer 绘制结果最后叠加在一起的过程也可以成为 layer 合成。</p></blockquote><p>下面我们看看具体代码实现。由于 Layer 的组合是一个标准的过程（唯一的不同是使用哪种ContainerLayer来作为父容器），PantingContext 中提供了一个 pushLayer 方法来执行组合过程，我们看看其实现源码：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">pushLayer</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">ContainerLayer</span><span style="color:#E1E4E8;"> childLayer, </span><span style="color:#79B8FF;">PaintingContextCallback</span><span style="color:#E1E4E8;"> painter, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset, { </span><span style="color:#79B8FF;">Rect</span><span style="color:#F97583;">?</span><span style="color:#E1E4E8;"> childPaintBounds }) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (childLayer.hasChildren) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    childLayer.</span><span style="color:#B392F0;">removeAllChildren</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">//下面两行是向Layer树中添加新Layer的标准操作，在之前小节中详细介绍过，忘记的话可以去查阅。</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#B392F0;">stopRecordingIfNeeded</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#B392F0;">appendLayer</span><span style="color:#E1E4E8;">(childLayer);</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">//通过新layer创建一个新的childContext对象</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">final</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> childContext </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#B392F0;">createChildContext</span><span style="color:#E1E4E8;">(childLayer, childPaintBounds </span><span style="color:#F97583;">??</span><span style="color:#E1E4E8;"> estimatedBounds);</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">//painter是绘制子节点的回调，我们需要将新的childContext对象传给它</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#B392F0;">painter</span><span style="color:#E1E4E8;">(childContext, offset);</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">//子节点绘制完成后获取绘制产物，将其保存到PictureLayer.picture中</span></span>
<span class="line"><span style="color:#E1E4E8;">  childContext.</span><span style="color:#B392F0;">stopRecordingIfNeeded</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">pushLayer</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">ContainerLayer</span><span style="color:#24292E;"> childLayer, </span><span style="color:#005CC5;">PaintingContextCallback</span><span style="color:#24292E;"> painter, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset, { </span><span style="color:#005CC5;">Rect</span><span style="color:#D73A49;">?</span><span style="color:#24292E;"> childPaintBounds }) {</span></span>
<span class="line"><span style="color:#24292E;">  </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (childLayer.hasChildren) {</span></span>
<span class="line"><span style="color:#24292E;">    childLayer.</span><span style="color:#6F42C1;">removeAllChildren</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">//下面两行是向Layer树中添加新Layer的标准操作，在之前小节中详细介绍过，忘记的话可以去查阅。</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6F42C1;">stopRecordingIfNeeded</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6F42C1;">appendLayer</span><span style="color:#24292E;">(childLayer);</span></span>
<span class="line"><span style="color:#24292E;">  </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">//通过新layer创建一个新的childContext对象</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">final</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> childContext </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6F42C1;">createChildContext</span><span style="color:#24292E;">(childLayer, childPaintBounds </span><span style="color:#D73A49;">??</span><span style="color:#24292E;"> estimatedBounds);</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">//painter是绘制子节点的回调，我们需要将新的childContext对象传给它</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6F42C1;">painter</span><span style="color:#24292E;">(childContext, offset);</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">//子节点绘制完成后获取绘制产物，将其保存到PictureLayer.picture中</span></span>
<span class="line"><span style="color:#24292E;">  childContext.</span><span style="color:#6F42C1;">stopRecordingIfNeeded</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>那么，我们只需要创建一个 TransformLayer 然后指定我们需要的旋转变换，然后直接调用 pushLayer 可以：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#6A737D;">// 创建一个持有 TransformLayer 的 handle.</span></span>
<span class="line"><span style="color:#F97583;">final</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">LayerHandle</span><span style="color:#E1E4E8;">&lt;</span><span style="color:#79B8FF;">TransformLayer</span><span style="color:#E1E4E8;">&gt; _transformLayer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">LayerHandle</span><span style="color:#E1E4E8;">&lt;</span><span style="color:#79B8FF;">TransformLayer</span><span style="color:#E1E4E8;">&gt;();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">_paintWithNewLayer</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset, </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;"> transform) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#6A737D;">//创建一个 TransformLayer，保存在handle中</span></span>
<span class="line"><span style="color:#E1E4E8;">    _transformLayer.layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> _transformLayer.layer </span><span style="color:#F97583;">??</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">TransformLayer</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">    _transformLayer.layer</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.transform </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> transform;</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span></span>
<span class="line"><span style="color:#E1E4E8;">    context.</span><span style="color:#B392F0;">pushLayer</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">      _transformLayer.layer</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">      _paintChild, </span><span style="color:#6A737D;">// 子节点绘制回调；添加完layer后，子节点会在新的layer上绘制</span></span>
<span class="line"><span style="color:#E1E4E8;">      offset,</span></span>
<span class="line"><span style="color:#E1E4E8;">      childPaintBounds</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">MatrixUtils</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">inverseTransformRect</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">        transform,</span></span>
<span class="line"><span style="color:#E1E4E8;">        offset </span><span style="color:#F97583;">&amp;</span><span style="color:#E1E4E8;"> size,</span></span>
<span class="line"><span style="color:#E1E4E8;">      ),</span></span>
<span class="line"><span style="color:#E1E4E8;">    );</span></span>
<span class="line"><span style="color:#E1E4E8;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;"> </span><span style="color:#6A737D;">// 子节点绘制回调 </span></span>
<span class="line"><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">_paintChild</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset) {</span></span>
<span class="line"><span style="color:#E1E4E8;">   context.</span><span style="color:#B392F0;">paintChild</span><span style="color:#E1E4E8;">(child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">, offset);</span></span>
<span class="line"><span style="color:#E1E4E8;"> }</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#6A737D;">// 创建一个持有 TransformLayer 的 handle.</span></span>
<span class="line"><span style="color:#D73A49;">final</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">LayerHandle</span><span style="color:#24292E;">&lt;</span><span style="color:#005CC5;">TransformLayer</span><span style="color:#24292E;">&gt; _transformLayer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">LayerHandle</span><span style="color:#24292E;">&lt;</span><span style="color:#005CC5;">TransformLayer</span><span style="color:#24292E;">&gt;();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">_paintWithNewLayer</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset, </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;"> transform) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6A737D;">//创建一个 TransformLayer，保存在handle中</span></span>
<span class="line"><span style="color:#24292E;">    _transformLayer.layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> _transformLayer.layer </span><span style="color:#D73A49;">??</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">TransformLayer</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">    _transformLayer.layer</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.transform </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> transform;</span></span>
<span class="line"><span style="color:#24292E;">    </span></span>
<span class="line"><span style="color:#24292E;">    context.</span><span style="color:#6F42C1;">pushLayer</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">      _transformLayer.layer</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">      _paintChild, </span><span style="color:#6A737D;">// 子节点绘制回调；添加完layer后，子节点会在新的layer上绘制</span></span>
<span class="line"><span style="color:#24292E;">      offset,</span></span>
<span class="line"><span style="color:#24292E;">      childPaintBounds</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">MatrixUtils</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">inverseTransformRect</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">        transform,</span></span>
<span class="line"><span style="color:#24292E;">        offset </span><span style="color:#D73A49;">&amp;</span><span style="color:#24292E;"> size,</span></span>
<span class="line"><span style="color:#24292E;">      ),</span></span>
<span class="line"><span style="color:#24292E;">    );</span></span>
<span class="line"><span style="color:#24292E;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;"> </span><span style="color:#6A737D;">// 子节点绘制回调 </span></span>
<span class="line"><span style="color:#24292E;"> </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">_paintChild</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset) {</span></span>
<span class="line"><span style="color:#24292E;">   context.</span><span style="color:#6F42C1;">paintChild</span><span style="color:#24292E;">(child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">, offset);</span></span>
<span class="line"><span style="color:#24292E;"> }</span></span></code></pre></div><p>然后我们需要在 paint 方法中判断一下子节点是否是绘制边界节点，如果是则需要走layer组合，如果不是则需要 走 layer 合成：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">paint</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">final</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;"> transform </span><span style="color:#F97583;">=</span></span>
<span class="line"><span style="color:#E1E4E8;">          </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">translationValues</span><span style="color:#E1E4E8;">(offset.dx, offset.dy, </span><span style="color:#79B8FF;">0.0</span><span style="color:#E1E4E8;">)</span></span>
<span class="line"><span style="color:#E1E4E8;">            ..</span><span style="color:#B392F0;">multiply</span><span style="color:#E1E4E8;">(_paintTransform</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">)</span></span>
<span class="line"><span style="color:#E1E4E8;">            ..</span><span style="color:#B392F0;">translate</span><span style="color:#E1E4E8;">(</span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;">offset.dx, </span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;">offset.dy);</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.isRepaintBoundary) { </span><span style="color:#6A737D;">// 添加判断</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#B392F0;">_paintWithNewLayer</span><span style="color:#E1E4E8;">(context, offset, transform);</span></span>
<span class="line"><span style="color:#E1E4E8;">      } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#B392F0;">_paint</span><span style="color:#E1E4E8;">(context, offset, transform);</span></span>
<span class="line"><span style="color:#E1E4E8;">      }</span></span>
<span class="line"><span style="color:#E1E4E8;">    } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">      _transformLayer.layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">    }</span></span>
<span class="line"><span style="color:#E1E4E8;"> }</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;"> </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">paint</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">final</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;"> transform </span><span style="color:#D73A49;">=</span></span>
<span class="line"><span style="color:#24292E;">          </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">translationValues</span><span style="color:#24292E;">(offset.dx, offset.dy, </span><span style="color:#005CC5;">0.0</span><span style="color:#24292E;">)</span></span>
<span class="line"><span style="color:#24292E;">            ..</span><span style="color:#6F42C1;">multiply</span><span style="color:#24292E;">(_paintTransform</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">)</span></span>
<span class="line"><span style="color:#24292E;">            ..</span><span style="color:#6F42C1;">translate</span><span style="color:#24292E;">(</span><span style="color:#D73A49;">-</span><span style="color:#24292E;">offset.dx, </span><span style="color:#D73A49;">-</span><span style="color:#24292E;">offset.dy);</span></span>
<span class="line"><span style="color:#24292E;">      </span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.isRepaintBoundary) { </span><span style="color:#6A737D;">// 添加判断</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#6F42C1;">_paintWithNewLayer</span><span style="color:#24292E;">(context, offset, transform);</span></span>
<span class="line"><span style="color:#24292E;">      } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#6F42C1;">_paint</span><span style="color:#24292E;">(context, offset, transform);</span></span>
<span class="line"><span style="color:#24292E;">      }</span></span>
<span class="line"><span style="color:#24292E;">    } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">      _transformLayer.layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">    }</span></span>
<span class="line"><span style="color:#24292E;"> }</span></span></code></pre></div><p>为了让代码看起看更清晰，我们将child不为空时的绘制逻辑逻辑封装一个 <code>pushTransform </code>函数里：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#79B8FF;">TransformLayer</span><span style="color:#F97583;">?</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">pushTransform</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context,</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">bool</span><span style="color:#E1E4E8;"> needsCompositing,</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset,</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;"> transform,</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">PaintingContextCallback</span><span style="color:#E1E4E8;"> painter, {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">TransformLayer</span><span style="color:#F97583;">?</span><span style="color:#E1E4E8;"> oldLayer,</span></span>
<span class="line"><span style="color:#E1E4E8;">  }) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">final</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;"> effectiveTransform </span><span style="color:#F97583;">=</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">translationValues</span><span style="color:#E1E4E8;">(offset.dx, offset.dy, </span><span style="color:#79B8FF;">0.0</span><span style="color:#E1E4E8;">)</span></span>
<span class="line"><span style="color:#E1E4E8;">          ..</span><span style="color:#B392F0;">multiply</span><span style="color:#E1E4E8;">(transform)</span></span>
<span class="line"><span style="color:#E1E4E8;">          ..</span><span style="color:#B392F0;">translate</span><span style="color:#E1E4E8;">(</span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;">offset.dx, </span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;">offset.dy);</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (needsCompositing) {</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">final</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">TransformLayer</span><span style="color:#E1E4E8;"> layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> oldLayer </span><span style="color:#F97583;">??</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">TransformLayer</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">      layer.transform </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> effectiveTransform;</span></span>
<span class="line"><span style="color:#E1E4E8;">      context.</span><span style="color:#B392F0;">pushLayer</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">        layer,</span></span>
<span class="line"><span style="color:#E1E4E8;">        painter,</span></span>
<span class="line"><span style="color:#E1E4E8;">        offset,</span></span>
<span class="line"><span style="color:#E1E4E8;">        childPaintBounds</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">MatrixUtils</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">inverseTransformRect</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">          effectiveTransform,</span></span>
<span class="line"><span style="color:#E1E4E8;">          context.estimatedBounds,</span></span>
<span class="line"><span style="color:#E1E4E8;">        ),</span></span>
<span class="line"><span style="color:#E1E4E8;">      );</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> layer;</span></span>
<span class="line"><span style="color:#E1E4E8;">    } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">      context.canvas</span></span>
<span class="line"><span style="color:#E1E4E8;">        ..</span><span style="color:#B392F0;">save</span><span style="color:#E1E4E8;">()</span></span>
<span class="line"><span style="color:#E1E4E8;">        ..</span><span style="color:#B392F0;">transform</span><span style="color:#E1E4E8;">(effectiveTransform.storage);</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#B392F0;">painter</span><span style="color:#E1E4E8;">(context, offset);</span></span>
<span class="line"><span style="color:#E1E4E8;">      context.canvas.</span><span style="color:#B392F0;">restore</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">    }</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#005CC5;">TransformLayer</span><span style="color:#D73A49;">?</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">pushTransform</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context,</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">bool</span><span style="color:#24292E;"> needsCompositing,</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset,</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;"> transform,</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">PaintingContextCallback</span><span style="color:#24292E;"> painter, {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">TransformLayer</span><span style="color:#D73A49;">?</span><span style="color:#24292E;"> oldLayer,</span></span>
<span class="line"><span style="color:#24292E;">  }) {</span></span>
<span class="line"><span style="color:#24292E;">    </span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">final</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;"> effectiveTransform </span><span style="color:#D73A49;">=</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">translationValues</span><span style="color:#24292E;">(offset.dx, offset.dy, </span><span style="color:#005CC5;">0.0</span><span style="color:#24292E;">)</span></span>
<span class="line"><span style="color:#24292E;">          ..</span><span style="color:#6F42C1;">multiply</span><span style="color:#24292E;">(transform)</span></span>
<span class="line"><span style="color:#24292E;">          ..</span><span style="color:#6F42C1;">translate</span><span style="color:#24292E;">(</span><span style="color:#D73A49;">-</span><span style="color:#24292E;">offset.dx, </span><span style="color:#D73A49;">-</span><span style="color:#24292E;">offset.dy);</span></span>
<span class="line"><span style="color:#24292E;">    </span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (needsCompositing) {</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">final</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">TransformLayer</span><span style="color:#24292E;"> layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> oldLayer </span><span style="color:#D73A49;">??</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">TransformLayer</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">      layer.transform </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> effectiveTransform;</span></span>
<span class="line"><span style="color:#24292E;">      context.</span><span style="color:#6F42C1;">pushLayer</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">        layer,</span></span>
<span class="line"><span style="color:#24292E;">        painter,</span></span>
<span class="line"><span style="color:#24292E;">        offset,</span></span>
<span class="line"><span style="color:#24292E;">        childPaintBounds</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">MatrixUtils</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">inverseTransformRect</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">          effectiveTransform,</span></span>
<span class="line"><span style="color:#24292E;">          context.estimatedBounds,</span></span>
<span class="line"><span style="color:#24292E;">        ),</span></span>
<span class="line"><span style="color:#24292E;">      );</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> layer;</span></span>
<span class="line"><span style="color:#24292E;">    } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">      context.canvas</span></span>
<span class="line"><span style="color:#24292E;">        ..</span><span style="color:#6F42C1;">save</span><span style="color:#24292E;">()</span></span>
<span class="line"><span style="color:#24292E;">        ..</span><span style="color:#6F42C1;">transform</span><span style="color:#24292E;">(effectiveTransform.storage);</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6F42C1;">painter</span><span style="color:#24292E;">(context, offset);</span></span>
<span class="line"><span style="color:#24292E;">      context.canvas.</span><span style="color:#6F42C1;">restore</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">    }</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span></code></pre></div><p>然后修改一下 paint 实现，直接调用 pushTransform 方法即可：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">paint</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#B392F0;">pushTransform</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">      context,</span></span>
<span class="line"><span style="color:#E1E4E8;">      child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.isRepaintBoundary,</span></span>
<span class="line"><span style="color:#E1E4E8;">      offset,</span></span>
<span class="line"><span style="color:#E1E4E8;">      _paintTransform</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">      _paintChild,</span></span>
<span class="line"><span style="color:#E1E4E8;">      oldLayer</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> _transformLayer.layer,</span></span>
<span class="line"><span style="color:#E1E4E8;">    );</span></span>
<span class="line"><span style="color:#E1E4E8;">  } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">    _transformLayer.layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">paint</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset) {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6F42C1;">pushTransform</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">      context,</span></span>
<span class="line"><span style="color:#24292E;">      child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.isRepaintBoundary,</span></span>
<span class="line"><span style="color:#24292E;">      offset,</span></span>
<span class="line"><span style="color:#24292E;">      _paintTransform</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">      _paintChild,</span></span>
<span class="line"><span style="color:#24292E;">      oldLayer</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> _transformLayer.layer,</span></span>
<span class="line"><span style="color:#24292E;">    );</span></span>
<span class="line"><span style="color:#24292E;">  } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">    _transformLayer.layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>是不是清晰多了，现在我们重新运行一下示例，效果和图14-17一样，A被成功放倒了！</p><p><img src="/assets/14-17.db7df290.png" alt="图14-17"></p><p>需要说明的是，其实 PaintingContext 已经帮我们封装好了 pushTransform 方法，我们可以直接使用它：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">paint</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    context.</span><span style="color:#B392F0;">pushTransform</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">      child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.isRepaintBoundary,</span></span>
<span class="line"><span style="color:#E1E4E8;">      offset,</span></span>
<span class="line"><span style="color:#E1E4E8;">      _paintTransform</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">      _paintChild,</span></span>
<span class="line"><span style="color:#E1E4E8;">      oldLayer</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> _transformLayer.layer,</span></span>
<span class="line"><span style="color:#E1E4E8;">    );</span></span>
<span class="line"><span style="color:#E1E4E8;">  } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">    _transformLayer.layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">paint</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset) {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">    context.</span><span style="color:#6F42C1;">pushTransform</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">      child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.isRepaintBoundary,</span></span>
<span class="line"><span style="color:#24292E;">      offset,</span></span>
<span class="line"><span style="color:#24292E;">      _paintTransform</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">      _paintChild,</span></span>
<span class="line"><span style="color:#24292E;">      oldLayer</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> _transformLayer.layer,</span></span>
<span class="line"><span style="color:#24292E;">    );</span></span>
<span class="line"><span style="color:#24292E;">  } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">    _transformLayer.layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>实际上，PaintingContext 针对常见的拥有变换功能的容器类Layer的组合都封装好了相应的方法，同时Flutter中已经预定了拥有相应变换功能的组件，下面是一个对应表：</p><table><thead><tr><th>Layer的名称</th><th>PaintingContext对应的方法</th><th>Widget</th></tr></thead><tbody><tr><td>ClipPathLayer</td><td>pushClipPath</td><td>ClipPath</td></tr><tr><td>OpacityLayer</td><td>pushOpacity</td><td>Opacity</td></tr><tr><td>ClipRRectLayer</td><td>pushClipRRect</td><td>ClipRRect</td></tr><tr><td>ClipRectLayer</td><td>pushClipRect</td><td>ClipRect</td></tr><tr><td>TransformLayer</td><td>pushTransform</td><td>RotatedBox、Transform</td></tr></tbody></table><h2 id="_14-8-2-什么时候需要合成-layer" tabindex="-1">14.8.2 什么时候需要合成 Layer ？ <a class="header-anchor" href="#_14-8-2-什么时候需要合成-layer" aria-label="Permalink to &quot;14.8.2 什么时候需要合成 Layer ？&quot;">​</a></h2><h3 id="_1-合成-layer-的原则" tabindex="-1">1. 合成 Layer 的原则 <a class="header-anchor" href="#_1-合成-layer-的原则" aria-label="Permalink to &quot;1. 合成 Layer 的原则&quot;">​</a></h3><p>通过上面的例子我们知道 CustomRotatedBox 的直接子节点是绘制边界节点时 CustomRotatedBox 中就需要合成 layer。实际上这只是一种特例，还有一些其他情况也需要 CustomRotatedBox 进行 Layer 合成，那什么时候需要 Layer 合成有没有一个一般性的普适原则？答案是：有！ 我们思考一下 CustomRotatedBox 中需要 Layer 合成的根本原因是什么？如果 CustomRotatedBox 的所有后代节点都共享的是同一个PictureLayer，但是，一旦有后代节点创建了新的PictureLayer，则绘制就会脱离了之前PictureLayer，因为不同的PictureLayer上的绘制是相互隔离的，是不能相互影响，所以为了使变换对所有后代节点对应的 PictureLayer 都生效，则我们就需要将所有后代节点的添加到同一个 ContainerLayer 中，所以就需要在 CustomRotatedBox 中先进行 Layer 合成。</p><p>综上，一个普适的原则就呼之欲出了：<strong>当后代节点会向 layer 树中添加新的绘制类Layer时，则父级的变换类组件中就需要合成 Layer</strong>。下面我们验证一下：</p><p>现在我们修改一下上面的示例，给 RepaintBoundary 添加一个 Center 父组件：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#79B8FF;">Widget</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">build</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">BuildContext</span><span style="color:#E1E4E8;"> context) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Center</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">    child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">CustomRotatedBox</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">      child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Center</span><span style="color:#E1E4E8;">( </span><span style="color:#6A737D;">// 新添加</span></span>
<span class="line"><span style="color:#E1E4E8;">        child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">RepaintBoundary</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">          child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Text</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">            </span><span style="color:#9ECBFF;">&quot;A&quot;</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">            textScaleFactor</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">5</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">          ),</span></span>
<span class="line"><span style="color:#E1E4E8;">        ),</span></span>
<span class="line"><span style="color:#E1E4E8;">      ),</span></span>
<span class="line"><span style="color:#E1E4E8;">    ),</span></span>
<span class="line"><span style="color:#E1E4E8;">  );</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#005CC5;">Widget</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">build</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">BuildContext</span><span style="color:#24292E;"> context) {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Center</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">    child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">CustomRotatedBox</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">      child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Center</span><span style="color:#24292E;">( </span><span style="color:#6A737D;">// 新添加</span></span>
<span class="line"><span style="color:#24292E;">        child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">RepaintBoundary</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">          child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Text</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">            </span><span style="color:#032F62;">&quot;A&quot;</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">            textScaleFactor</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">5</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">          ),</span></span>
<span class="line"><span style="color:#24292E;">        ),</span></span>
<span class="line"><span style="color:#24292E;">      ),</span></span>
<span class="line"><span style="color:#24292E;">    ),</span></span>
<span class="line"><span style="color:#24292E;">  );</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>因为 CustomRotatedBox 中只判断了其直接子节点的<code>child!.isRepaintBoundary</code> 为 true时，才会进行 layer 合成，而现在它的直接子节点是Center，所以该判断会是false，则不会进行 layer 合成。但是根据我们上面得出的结论，RepaintBoundary 作为CustomRotatedBox 的后代节点且会向 layer 树中添加新 layer 时就需要进行 layer合成，而本例中是应该合成layer但实际上却没有合成，所以预期是不能将 &quot;A&quot; 放倒的，运行后发现效果和之前的图14-18相同：</p><p><img src="/assets/14-18.3f9bb9ab.png" alt="图14-18"></p><p>果然 ”A“ 并没有被放倒！看来我们的 CustomRotatedBox 还是需要继续修改。解决这个问题并不难，我们在判断是否需要进行 Layer 合成时，要去遍历整个子树，看看否存在绘制边界节点，如果是则合成，反之则否。为此，我们新定义一个在子树上查找是否存在绘制边界节点的 <code>needCompositing() </code> 方法：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#6A737D;">//子树中递归查找是否存在绘制边界</span></span>
<span class="line"><span style="color:#B392F0;">needCompositing</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">bool</span><span style="color:#E1E4E8;"> result </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">false</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#B392F0;">_visit</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">RenderObject</span><span style="color:#E1E4E8;"> child) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child.isRepaintBoundary) {</span></span>
<span class="line"><span style="color:#E1E4E8;">      result </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">true</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> ;</span></span>
<span class="line"><span style="color:#E1E4E8;">    } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#6A737D;">//递归查找</span></span>
<span class="line"><span style="color:#E1E4E8;">      child.</span><span style="color:#B392F0;">visitChildren</span><span style="color:#E1E4E8;">(_visit);</span></span>
<span class="line"><span style="color:#E1E4E8;">    }</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">//遍历子节点</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#B392F0;">visitChildren</span><span style="color:#E1E4E8;">(_visit);</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> result;</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#6A737D;">//子树中递归查找是否存在绘制边界</span></span>
<span class="line"><span style="color:#6F42C1;">needCompositing</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">bool</span><span style="color:#24292E;"> result </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">false</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6F42C1;">_visit</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">RenderObject</span><span style="color:#24292E;"> child) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child.isRepaintBoundary) {</span></span>
<span class="line"><span style="color:#24292E;">      result </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">true</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> ;</span></span>
<span class="line"><span style="color:#24292E;">    } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6A737D;">//递归查找</span></span>
<span class="line"><span style="color:#24292E;">      child.</span><span style="color:#6F42C1;">visitChildren</span><span style="color:#24292E;">(_visit);</span></span>
<span class="line"><span style="color:#24292E;">    }</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">//遍历子节点</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6F42C1;">visitChildren</span><span style="color:#24292E;">(_visit);</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> result;</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>然后需要修改一下 paint 实现：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">paint</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    context.</span><span style="color:#B392F0;">pushTransform</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#B392F0;">needCompositing</span><span style="color:#E1E4E8;">(), </span><span style="color:#6A737D;">//子树是否存在绘制边界节点</span></span>
<span class="line"><span style="color:#E1E4E8;">      offset,</span></span>
<span class="line"><span style="color:#E1E4E8;">      _paintTransform</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">      _paintChild,</span></span>
<span class="line"><span style="color:#E1E4E8;">      oldLayer</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> _transformLayer.layer,</span></span>
<span class="line"><span style="color:#E1E4E8;">    );</span></span>
<span class="line"><span style="color:#E1E4E8;">  } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">    _transformLayer.layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">paint</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset) {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">    context.</span><span style="color:#6F42C1;">pushTransform</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6F42C1;">needCompositing</span><span style="color:#24292E;">(), </span><span style="color:#6A737D;">//子树是否存在绘制边界节点</span></span>
<span class="line"><span style="color:#24292E;">      offset,</span></span>
<span class="line"><span style="color:#24292E;">      _paintTransform</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">      _paintChild,</span></span>
<span class="line"><span style="color:#24292E;">      oldLayer</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> _transformLayer.layer,</span></span>
<span class="line"><span style="color:#24292E;">    );</span></span>
<span class="line"><span style="color:#24292E;">  } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">    _transformLayer.layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>现在，我们再来运行一下demo，运行后效果和图14-17相同：</p><p><img src="/assets/14-17.db7df290.png" alt="图14-17"></p><p>又成功放倒了！但还有问题，我们继续往下看。</p><h3 id="_2-alwaysneedscompositing" tabindex="-1">2. alwaysNeedsCompositing <a class="header-anchor" href="#_2-alwaysneedscompositing" aria-label="Permalink to &quot;2. alwaysNeedsCompositing&quot;">​</a></h3><p>我们考虑一下这种情况：如果 CustomRotatedBox 的后代节点中没有绘制边界节点，但是有后代节点向 layer 树中添加了新的 layer。这种情况下，按照我们之前得出的结论 CustomRotatedBox 中也是需要进行 layer 合成的，但 CustomRotatedBox 实际上并没有。问题知道了，但是这个问题却不好解决，原因是我们在 CustomRotatedBox 中遍历后代节点时，是无法知道非绘制边界节点是否往 layer树中添加了新的 layer。怎么办呢？Flutter是通过约定来解决这个问题的：</p><ol><li><p>RenderObject 中定义了一个布尔类型 <code>alwaysNeedsCompositing</code> 属性。</p></li><li><p>约定：自定义组件中，如果组件 <code>isRepaintBoundary</code> 为 <code>false</code> 时，在绘制时要会向 layer 树中添加新的 layer的话，要将 <code>alwaysNeedsCompositing</code> 置为 <code>true</code> 。</p></li></ol><p>开发者在自定义组件时应该遵守这个规范。根据此规范，CustomRotatedBox 中我们在子树中递归查找时的判断条件就可以改为：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#E1E4E8;">child.isRepaintBoundary </span><span style="color:#F97583;">||</span><span style="color:#E1E4E8;"> child.alwaysNeedsCompositing</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#24292E;">child.isRepaintBoundary </span><span style="color:#D73A49;">||</span><span style="color:#24292E;"> child.alwaysNeedsCompositing</span></span></code></pre></div><p>最终 我们的<code>needCompositing</code> 实现如下：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#6A737D;">//子树中递归查找是否存在绘制边界</span></span>
<span class="line"><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">needCompositing</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">bool</span><span style="color:#E1E4E8;"> result </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">false</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#B392F0;">_visit</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">RenderObject</span><span style="color:#E1E4E8;"> child) {</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#6A737D;">// 修改判断条件改为</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child.isRepaintBoundary </span><span style="color:#F97583;">||</span><span style="color:#E1E4E8;"> child.alwaysNeedsCompositing) {</span></span>
<span class="line"><span style="color:#E1E4E8;">        result </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">true</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> ;</span></span>
<span class="line"><span style="color:#E1E4E8;">      } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">        child.</span><span style="color:#B392F0;">visitChildren</span><span style="color:#E1E4E8;">(_visit);</span></span>
<span class="line"><span style="color:#E1E4E8;">      }</span></span>
<span class="line"><span style="color:#E1E4E8;">    }</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#B392F0;">visitChildren</span><span style="color:#E1E4E8;">(_visit);</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> result;</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#6A737D;">//子树中递归查找是否存在绘制边界</span></span>
<span class="line"><span style="color:#24292E;"> </span><span style="color:#6F42C1;">needCompositing</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">bool</span><span style="color:#24292E;"> result </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">false</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6F42C1;">_visit</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">RenderObject</span><span style="color:#24292E;"> child) {</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6A737D;">// 修改判断条件改为</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child.isRepaintBoundary </span><span style="color:#D73A49;">||</span><span style="color:#24292E;"> child.alwaysNeedsCompositing) {</span></span>
<span class="line"><span style="color:#24292E;">        result </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">true</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> ;</span></span>
<span class="line"><span style="color:#24292E;">      } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">        child.</span><span style="color:#6F42C1;">visitChildren</span><span style="color:#24292E;">(_visit);</span></span>
<span class="line"><span style="color:#24292E;">      }</span></span>
<span class="line"><span style="color:#24292E;">    }</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6F42C1;">visitChildren</span><span style="color:#24292E;">(_visit);</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> result;</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span></code></pre></div><blockquote><p>注意：这要求非绘制节点组件在向 layer 树中添加 layer 时必须的让自身的 <code>alwaysNeedsCompositing</code> 值为 <code>ture</code> .</p></blockquote><p>下面我们看一下 flutter 中 Opacity 组件的实现。</p><h3 id="_3-opacity-解析" tabindex="-1">3. Opacity 解析 <a class="header-anchor" href="#_3-opacity-解析" aria-label="Permalink to &quot;3. Opacity 解析&quot;">​</a></h3><p>Opacity 可以对子树进行透明度控制，这个效果通过 canvas 是很难实现的，所以 flutter 中直接使用了 OffsetLayer 合成的方式来实现：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">class</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">RenderOpacity</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">extends</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">RenderProxyBox</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 本组件是非绘制边界节点，但会在部分透明的情况下向layer树中添加新的Layer，所以部分透明时要返回 true</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">bool</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> alwaysNeedsCompositing </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> child </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">&amp;&amp;</span><span style="color:#E1E4E8;"> (_alpha </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">0</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">&amp;&amp;</span><span style="color:#E1E4E8;"> _alpha </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">255</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">paint</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (_alpha </span><span style="color:#F97583;">==</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">0</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#6A737D;">// 完全透明，则没必要再绘制子节点了</span></span>
<span class="line"><span style="color:#E1E4E8;">        layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">      }</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (_alpha </span><span style="color:#F97583;">==</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">255</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#6A737D;">// 完全不透明，则不需要变换处理，直接绘制子节点即可</span></span>
<span class="line"><span style="color:#E1E4E8;">        layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">        context.</span><span style="color:#B392F0;">paintChild</span><span style="color:#E1E4E8;">(child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">, offset);</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">      }</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#6A737D;">// 部分透明，需要通过OffsetLayer来处理，会向layer树中添加新 layer</span></span>
<span class="line"><span style="color:#E1E4E8;">      layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> context.</span><span style="color:#B392F0;">pushOpacity</span><span style="color:#E1E4E8;">(offset, _alpha, </span><span style="color:#79B8FF;">super</span><span style="color:#E1E4E8;">.paint, oldLayer</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> layer </span><span style="color:#F97583;">as</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">OpacityLayer</span><span style="color:#F97583;">?</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">    }</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">class</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">RenderOpacity</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">extends</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">RenderProxyBox</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">  </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 本组件是非绘制边界节点，但会在部分透明的情况下向layer树中添加新的Layer，所以部分透明时要返回 true</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">bool</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> alwaysNeedsCompositing </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> child </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">&amp;&amp;</span><span style="color:#24292E;"> (_alpha </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">0</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">&amp;&amp;</span><span style="color:#24292E;"> _alpha </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">255</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">  </span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">paint</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (_alpha </span><span style="color:#D73A49;">==</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">0</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#6A737D;">// 完全透明，则没必要再绘制子节点了</span></span>
<span class="line"><span style="color:#24292E;">        layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#D73A49;">return</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">      }</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (_alpha </span><span style="color:#D73A49;">==</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">255</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#6A737D;">// 完全不透明，则不需要变换处理，直接绘制子节点即可</span></span>
<span class="line"><span style="color:#24292E;">        layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">        context.</span><span style="color:#6F42C1;">paintChild</span><span style="color:#24292E;">(child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">, offset);</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#D73A49;">return</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">      }</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6A737D;">// 部分透明，需要通过OffsetLayer来处理，会向layer树中添加新 layer</span></span>
<span class="line"><span style="color:#24292E;">      layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> context.</span><span style="color:#6F42C1;">pushOpacity</span><span style="color:#24292E;">(offset, _alpha, </span><span style="color:#005CC5;">super</span><span style="color:#24292E;">.paint, oldLayer</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> layer </span><span style="color:#D73A49;">as</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">OpacityLayer</span><span style="color:#D73A49;">?</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">    }</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><h3 id="_4-优化" tabindex="-1">4. 优化 <a class="header-anchor" href="#_4-优化" aria-label="Permalink to &quot;4. 优化&quot;">​</a></h3><p>注意，上面我们通过 CustomRotatedBox 演示了变换类组件的核心原理，不过还有一些优化的地方，比如：</p><ol><li>变换类组件中，遍历子树以确定是否需要 layer 合成是变换类组件的通用逻辑，不需要在每个组件里都实现一遍。</li><li>不是每一次重绘都需要去遍历子树，比如可以在初始化时遍历一次，然后将结果缓存，如果后续有变化，再重新遍历更新即可，此时直接使用缓存的结果。</li></ol><p>Flutter 也考虑到了这个问题，于是便有了flushCompositingBits 方法，我们下面来正式介绍它。</p><h2 id="_14-8-3-flushcompositingbits" tabindex="-1">14.8.3 flushCompositingBits <a class="header-anchor" href="#_14-8-3-flushcompositingbits" aria-label="Permalink to &quot;14.8.3 flushCompositingBits&quot;">​</a></h2><p>每一个节点（RenderObject中）都有一个<code>_needsCompositing</code> 字段，该字段用于缓存当前节点在绘制子节点时是否需要合成 layer。flushCompositingBits 的功能就是在节点树初始化和子树中合成信息发生变化时来重新遍历节点树，更新每一个节点的<code>_needsCompositing</code> 值。可以发现：</p><ol><li>递归遍历子树的逻辑抽到了 flushCompositingBits 中，不需要组件单独实现。</li><li>不需要每一次重绘都遍历子树了，只需要在初始化和发生变化时重新遍历。</li></ol><p>完美的解决了我们之前提出的问题，下面我们看一下具体实现：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">flushCompositingBits</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 对需要更新合成信息的节点按照节点在节点树中的深度排序</span></span>
<span class="line"><span style="color:#E1E4E8;">  _nodesNeedingCompositingBitsUpdate.</span><span style="color:#B392F0;">sort</span><span style="color:#E1E4E8;">((a,b) </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> a.depth </span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;"> b.depth);</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">for</span><span style="color:#E1E4E8;"> (</span><span style="color:#F97583;">final</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">RenderObject</span><span style="color:#E1E4E8;"> node </span><span style="color:#F97583;">in</span><span style="color:#E1E4E8;"> _nodesNeedingCompositingBitsUpdate) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (node._needsCompositingBitsUpdate </span><span style="color:#F97583;">&amp;&amp;</span><span style="color:#E1E4E8;"> node.owner </span><span style="color:#F97583;">==</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">this</span><span style="color:#E1E4E8;">)</span></span>
<span class="line"><span style="color:#E1E4E8;">      node.</span><span style="color:#B392F0;">_updateCompositingBits</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">//更新合成信息</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">  _nodesNeedingCompositingBitsUpdate.</span><span style="color:#B392F0;">clear</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">flushCompositingBits</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 对需要更新合成信息的节点按照节点在节点树中的深度排序</span></span>
<span class="line"><span style="color:#24292E;">  _nodesNeedingCompositingBitsUpdate.</span><span style="color:#6F42C1;">sort</span><span style="color:#24292E;">((a,b) </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> a.depth </span><span style="color:#D73A49;">-</span><span style="color:#24292E;"> b.depth);</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">for</span><span style="color:#24292E;"> (</span><span style="color:#D73A49;">final</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">RenderObject</span><span style="color:#24292E;"> node </span><span style="color:#D73A49;">in</span><span style="color:#24292E;"> _nodesNeedingCompositingBitsUpdate) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (node._needsCompositingBitsUpdate </span><span style="color:#D73A49;">&amp;&amp;</span><span style="color:#24292E;"> node.owner </span><span style="color:#D73A49;">==</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">this</span><span style="color:#24292E;">)</span></span>
<span class="line"><span style="color:#24292E;">      node.</span><span style="color:#6F42C1;">_updateCompositingBits</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">//更新合成信息</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">  _nodesNeedingCompositingBitsUpdate.</span><span style="color:#6F42C1;">clear</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>RenderObject 的 <code>_updateCompositingBits</code> 方法的功能就是递归遍历子树确定如果每一个节点的<code>_needsCompositing</code> 值：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">_updateCompositingBits</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">_needsCompositingBitsUpdate)</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">final</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">bool</span><span style="color:#E1E4E8;"> oldNeedsCompositing </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> _needsCompositing;</span></span>
<span class="line"><span style="color:#E1E4E8;">  _needsCompositing </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">false</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 递归遍历查找子树, 如果有孩子节点 needsCompositing 为true，则更新 _needsCompositing 值</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#B392F0;">visitChildren</span><span style="color:#E1E4E8;">((</span><span style="color:#79B8FF;">RenderObject</span><span style="color:#E1E4E8;"> child) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    child.</span><span style="color:#B392F0;">_updateCompositingBits</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">//递归执行</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child.needsCompositing)</span></span>
<span class="line"><span style="color:#E1E4E8;">      _needsCompositing </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">true</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  });</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 这行我们上面讲过</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (isRepaintBoundary </span><span style="color:#F97583;">||</span><span style="color:#E1E4E8;"> alwaysNeedsCompositing)</span></span>
<span class="line"><span style="color:#E1E4E8;">    _needsCompositing </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">true</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (oldNeedsCompositing </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> _needsCompositing)</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#B392F0;">markNeedsPaint</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  _needsCompositingBitsUpdate </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">false</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">_updateCompositingBits</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">_needsCompositingBitsUpdate)</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">return</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">final</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">bool</span><span style="color:#24292E;"> oldNeedsCompositing </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> _needsCompositing;</span></span>
<span class="line"><span style="color:#24292E;">  _needsCompositing </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">false</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 递归遍历查找子树, 如果有孩子节点 needsCompositing 为true，则更新 _needsCompositing 值</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6F42C1;">visitChildren</span><span style="color:#24292E;">((</span><span style="color:#005CC5;">RenderObject</span><span style="color:#24292E;"> child) {</span></span>
<span class="line"><span style="color:#24292E;">    child.</span><span style="color:#6F42C1;">_updateCompositingBits</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">//递归执行</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child.needsCompositing)</span></span>
<span class="line"><span style="color:#24292E;">      _needsCompositing </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">true</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  });</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 这行我们上面讲过</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (isRepaintBoundary </span><span style="color:#D73A49;">||</span><span style="color:#24292E;"> alwaysNeedsCompositing)</span></span>
<span class="line"><span style="color:#24292E;">    _needsCompositing </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">true</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (oldNeedsCompositing </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> _needsCompositing)</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6F42C1;">markNeedsPaint</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  _needsCompositingBitsUpdate </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">false</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>执行完毕后，每一个节点的<code> _needsCompositing</code> 就确定了，我们在绘制时只需要判断一下当前的 <code>needsCompositing</code>（一个getter，会直接返回<code> _needsCompositing</code> ） 就能知道子树是否存在剥离layer了。这样的话，我们可以再优化一下 CustomRenderRotatedBox 的实现，最终的实现如下：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">class</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">CustomRenderRotatedBox</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">extends</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">RenderBox</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">with</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">RenderObjectWithChildMixin</span><span style="color:#E1E4E8;">&lt;</span><span style="color:#79B8FF;">RenderBox</span><span style="color:#E1E4E8;">&gt; {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#F97583;">?</span><span style="color:#E1E4E8;"> _paintTransform;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">performLayout</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">    _paintTransform </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">      child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">layout</span><span style="color:#E1E4E8;">(constraints, parentUsesSize</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">true</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">      size </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.size;</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#6A737D;">//根据子组件大小计算出旋转矩阵</span></span>
<span class="line"><span style="color:#E1E4E8;">      _paintTransform </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">identity</span><span style="color:#E1E4E8;">()</span></span>
<span class="line"><span style="color:#E1E4E8;">        ..</span><span style="color:#B392F0;">translate</span><span style="color:#E1E4E8;">(size.width </span><span style="color:#F97583;">/</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">2.0</span><span style="color:#E1E4E8;">, size.height </span><span style="color:#F97583;">/</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">2.0</span><span style="color:#E1E4E8;">)</span></span>
<span class="line"><span style="color:#E1E4E8;">        ..</span><span style="color:#B392F0;">rotateZ</span><span style="color:#E1E4E8;">(math.pi </span><span style="color:#F97583;">/</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">2</span><span style="color:#E1E4E8;">)</span></span>
<span class="line"><span style="color:#E1E4E8;">        ..</span><span style="color:#B392F0;">translate</span><span style="color:#E1E4E8;">(</span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;">child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.size.width </span><span style="color:#F97583;">/</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">2.0</span><span style="color:#E1E4E8;">, </span><span style="color:#F97583;">-</span><span style="color:#E1E4E8;">child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.size.height </span><span style="color:#F97583;">/</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">2.0</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">    } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">      size </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> constraints.smallest;</span></span>
<span class="line"><span style="color:#E1E4E8;">    }</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">final</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">LayerHandle</span><span style="color:#E1E4E8;">&lt;</span><span style="color:#79B8FF;">TransformLayer</span><span style="color:#E1E4E8;">&gt; _transformLayer </span><span style="color:#F97583;">=</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">LayerHandle</span><span style="color:#E1E4E8;">&lt;</span><span style="color:#79B8FF;">TransformLayer</span><span style="color:#E1E4E8;">&gt;();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">_paintChild</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#B392F0;">print</span><span style="color:#E1E4E8;">(</span><span style="color:#9ECBFF;">&quot;paint child&quot;</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">    context.</span><span style="color:#B392F0;">paintChild</span><span style="color:#E1E4E8;">(child</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">, offset);</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">paint</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">PaintingContext</span><span style="color:#E1E4E8;"> context, </span><span style="color:#79B8FF;">Offset</span><span style="color:#E1E4E8;"> offset) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (child </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">     _transformLayer.layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> context.</span><span style="color:#B392F0;">pushTransform</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">        needsCompositing, </span><span style="color:#6A737D;">// pipelineOwner.flushCompositingBits(); 执行后这个值就能确定</span></span>
<span class="line"><span style="color:#E1E4E8;">        offset,</span></span>
<span class="line"><span style="color:#E1E4E8;">        _paintTransform</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">        _paintChild,</span></span>
<span class="line"><span style="color:#E1E4E8;">        oldLayer</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> _transformLayer.layer,</span></span>
<span class="line"><span style="color:#E1E4E8;">      );</span></span>
<span class="line"><span style="color:#E1E4E8;">    } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">      _transformLayer.layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">    }</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">dispose</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">    _transformLayer.layer </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">super</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">dispose</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">applyPaintTransform</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">RenderBox</span><span style="color:#E1E4E8;"> child, </span><span style="color:#79B8FF;">Matrix4</span><span style="color:#E1E4E8;"> transform) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (_paintTransform </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">) transform.</span><span style="color:#B392F0;">multiply</span><span style="color:#E1E4E8;">(_paintTransform</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">super</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">applyPaintTransform</span><span style="color:#E1E4E8;">(child, transform);</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">class</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">CustomRenderRotatedBox</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">extends</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">RenderBox</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">with</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">RenderObjectWithChildMixin</span><span style="color:#24292E;">&lt;</span><span style="color:#005CC5;">RenderBox</span><span style="color:#24292E;">&gt; {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">Matrix4</span><span style="color:#D73A49;">?</span><span style="color:#24292E;"> _paintTransform;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">performLayout</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">    _paintTransform </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">      child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">layout</span><span style="color:#24292E;">(constraints, parentUsesSize</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">true</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">      size </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.size;</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6A737D;">//根据子组件大小计算出旋转矩阵</span></span>
<span class="line"><span style="color:#24292E;">      _paintTransform </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">identity</span><span style="color:#24292E;">()</span></span>
<span class="line"><span style="color:#24292E;">        ..</span><span style="color:#6F42C1;">translate</span><span style="color:#24292E;">(size.width </span><span style="color:#D73A49;">/</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">2.0</span><span style="color:#24292E;">, size.height </span><span style="color:#D73A49;">/</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">2.0</span><span style="color:#24292E;">)</span></span>
<span class="line"><span style="color:#24292E;">        ..</span><span style="color:#6F42C1;">rotateZ</span><span style="color:#24292E;">(math.pi </span><span style="color:#D73A49;">/</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">2</span><span style="color:#24292E;">)</span></span>
<span class="line"><span style="color:#24292E;">        ..</span><span style="color:#6F42C1;">translate</span><span style="color:#24292E;">(</span><span style="color:#D73A49;">-</span><span style="color:#24292E;">child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.size.width </span><span style="color:#D73A49;">/</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">2.0</span><span style="color:#24292E;">, </span><span style="color:#D73A49;">-</span><span style="color:#24292E;">child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.size.height </span><span style="color:#D73A49;">/</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">2.0</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">    } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">      size </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> constraints.smallest;</span></span>
<span class="line"><span style="color:#24292E;">    }</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">final</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">LayerHandle</span><span style="color:#24292E;">&lt;</span><span style="color:#005CC5;">TransformLayer</span><span style="color:#24292E;">&gt; _transformLayer </span><span style="color:#D73A49;">=</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">LayerHandle</span><span style="color:#24292E;">&lt;</span><span style="color:#005CC5;">TransformLayer</span><span style="color:#24292E;">&gt;();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">_paintChild</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6F42C1;">print</span><span style="color:#24292E;">(</span><span style="color:#032F62;">&quot;paint child&quot;</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">    context.</span><span style="color:#6F42C1;">paintChild</span><span style="color:#24292E;">(child</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">, offset);</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">paint</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">PaintingContext</span><span style="color:#24292E;"> context, </span><span style="color:#005CC5;">Offset</span><span style="color:#24292E;"> offset) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (child </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">     _transformLayer.layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> context.</span><span style="color:#6F42C1;">pushTransform</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">        needsCompositing, </span><span style="color:#6A737D;">// pipelineOwner.flushCompositingBits(); 执行后这个值就能确定</span></span>
<span class="line"><span style="color:#24292E;">        offset,</span></span>
<span class="line"><span style="color:#24292E;">        _paintTransform</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">        _paintChild,</span></span>
<span class="line"><span style="color:#24292E;">        oldLayer</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> _transformLayer.layer,</span></span>
<span class="line"><span style="color:#24292E;">      );</span></span>
<span class="line"><span style="color:#24292E;">    } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">      _transformLayer.layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">    }</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">dispose</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">    _transformLayer.layer </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">super</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">dispose</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">applyPaintTransform</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">RenderBox</span><span style="color:#24292E;"> child, </span><span style="color:#005CC5;">Matrix4</span><span style="color:#24292E;"> transform) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (_paintTransform </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">) transform.</span><span style="color:#6F42C1;">multiply</span><span style="color:#24292E;">(_paintTransform</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">super</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">applyPaintTransform</span><span style="color:#24292E;">(child, transform);</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>是不是简洁清晰了很多！</p><h3 id="再论-flushcompositingbits" tabindex="-1">再论 flushCompositingBits <a class="header-anchor" href="#再论-flushcompositingbits" aria-label="Permalink to &quot;再论 flushCompositingBits&quot;">​</a></h3><p>现在，我们思考一下引入 flushCompositingBits 的根本原因是什么？假如我们在变换类容器中始终采用合成 layer 的方式来对子树应用变换效果，也就是说不再使用 canvas 进行变换，这样的话 flushCompositingBits 也就没必要存在了，为什么一定要 flushCompositingBits 呢？根本原因就是：如果在变换类组件中一刀切的使用合成 layer 方式的话，每遇到一个变换类组件则至少会再创建一个 layer，这样的话，最终 layer 树上的layer数量就会变多。我们之前说过对子树应用的变换效果既能通过 Canvas 实现也能通过容器类Layer实现时，建议使用Canvas 。这是因为每新建一个 layer 都会有额外的开销，所以我们只应该在无法通过 Canvas 来实现子树变化效果时再通过Layer 合成的方式来实现。综上，我们可以发现<strong>引入 flushCompositingBits 的根本原因其实是为了减少 layer的数量</strong>。</p><p>另外，flushCompositingBits 的执行过程只是做标记，并没有进行层的合成，真正的合成是在绘制时（组件的 paint 方法中）。</p><h2 id="_14-8-4-总结" tabindex="-1">14.8.4 总结 <a class="header-anchor" href="#_14-8-4-总结" aria-label="Permalink to &quot;14.8.4 总结&quot;">​</a></h2><ol><li><p>只有组件树中有变换类容器时，才有可能需要重新合成 layer；如果没有变换类组件，则不需要。</p></li><li><p>当变换类容器的后代节点会向 layer 树中添加新的绘制类 layer 时，则变换类组件中就需要合成 layer。</p></li><li><p>引入 flushCompositingBits 的根本原因是为了减少 layer 的数量。</p></li></ol></div></div></main><footer class="VPDocFooter" data-v-6b87e69f data-v-ef5dee53><!--[--><!--]--><div class="edit-info" data-v-ef5dee53><div class="edit-link" data-v-ef5dee53><a class="VPLink link vp-external-link-icon no-icon edit-link-button" href="https://gitee.com/luoguangguang/note/tree/master/docs/largeFrontEnd/flutter/chapter14/compositing.md" target="_blank" rel="noreferrer" data-v-ef5dee53><!--[--><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" class="edit-link-icon" aria-label="edit icon" data-v-ef5dee53><path d="M18,23H4c-1.7,0-3-1.3-3-3V6c0-1.7,1.3-3,3-3h7c0.6,0,1,0.4,1,1s-0.4,1-1,1H4C3.4,5,3,5.4,3,6v14c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1v-7c0-0.6,0.4-1,1-1s1,0.4,1,1v7C21,21.7,19.7,23,18,23z"></path><path d="M8,17c-0.3,0-0.5-0.1-0.7-0.3C7,16.5,6.9,16.1,7,15.8l1-4c0-0.2,0.1-0.3,0.3-0.5l9.5-9.5c1.2-1.2,3.2-1.2,4.4,0c1.2,1.2,1.2,3.2,0,4.4l-9.5,9.5c-0.1,0.1-0.3,0.2-0.5,0.3l-4,1C8.2,17,8.1,17,8,17zM9.9,12.5l-0.5,2.1l2.1-0.5l9.3-9.3c0.4-0.4,0.4-1.1,0-1.6c-0.4-0.4-1.2-0.4-1.6,0l0,0L9.9,12.5z M18.5,2.5L18.5,2.5L18.5,2.5z"></path></svg> 在Gitee上参与编辑此页<!--]--></a></div><!----></div><nav class="prev-next" data-v-ef5dee53><div class="pager" data-v-ef5dee53><!----></div><div class="pager" data-v-ef5dee53><a class="pager-link next" href="./largeFrontEnd/flutter/chapter1/index.html" data-v-ef5dee53><span class="desc" data-v-ef5dee53>Next page</span><span class="title" data-v-ef5dee53>第一章 初识Flutter</span></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><footer class="VPFooter has-sidebar" data-v-1919c326 data-v-e03eb2e1><div class="container" data-v-e03eb2e1><p class="message" data-v-e03eb2e1>Released under the MIT License.</p><p class="copyright" data-v-e03eb2e1>Copyright © 2023-present LG of GZU</p></div></footer><!--[--><!--]--></div></div>
    <script>window.__VP_HASH_MAP__=JSON.parse("{\"backend_index.md\":\"0cb07c7c\",\"largefrontend_electron_index.md\":\"adab8e13\",\"largefrontend_flutter_chapter1_index.md\":\"5106cc23\",\"index.md\":\"20a66851\",\"largefrontend_flutter_chapter10_intro.md\":\"5756d1e8\",\"largefrontend_flutter_chapter10_index.md\":\"87e47f16\",\"largefrontend_flutter_chapter1_flutter_intro.md\":\"9ad5fc78\",\"largefrontend_flutter_chapter12_package_and_plugin.md\":\"c2f14cd7\",\"largefrontend_flutter_chapter12_flutter_web.md\":\"f4b6b743\",\"largefrontend_flutter_chapter10_combine.md\":\"333319d8\",\"largefrontend_flutter_chapter14_flutter_ui_system.md\":\"a1011f7b\",\"largefrontend_flutter_chapter11_index.md\":\"35dcdd7c\",\"largefrontend_flutter_chapter12_index.md\":\"890b799c\",\"largefrontend_flutter_chapter13_index.md\":\"6db9b579\",\"largefrontend_flutter_chapter14_index.md\":\"87f7da6b\",\"largefrontend_flutter_chapter1_mobile_development_intro.md\":\"40c15ac2\",\"largefrontend_flutter_chapter10_turn_box.md\":\"6a792fe1\",\"largefrontend_flutter_chapter3_index.md\":\"bc1d0de5\",\"largefrontend_flutter_chapter4_stack.md\":\"3f5c3186\",\"largefrontend_flutter_chapter1_install_flutter.md\":\"4543c368\",\"largefrontend_flutter_chapter14_paint.md\":\"8f7289b0\",\"largefrontend_flutter_chapter5_padding.md\":\"9fadce05\",\"largefrontend_flutter_chapter4_index.md\":\"f9b298f7\",\"largefrontend_flutter_chapter2_flutter_package_mgr.md\":\"4a3b7161\",\"largefrontend_flutter_chapter11_socket.md\":\"e8264e44\",\"largefrontend_flutter_chapter4_intro.md\":\"1341b5fd\",\"largefrontend_flutter_chapter14_update.md\":\"e4d2c2f0\",\"largefrontend_flutter_chapter5_index.md\":\"455baa0a\",\"largefrontend_flutter_chapter4_alignment.md\":\"a6596610\",\"largefrontend_flutter_chapter13_multi_languages_support.md\":\"c113238d\",\"largefrontend_flutter_chapter11_dio.md\":\"64f4e813\",\"largefrontend_flutter_chapter2_first_flutter_app.md\":\"b650181d\",\"largefrontend_flutter_chapter2_flutter_app_debug.md\":\"67a72433\",\"largefrontend_flutter_chapter2_state_manage.md\":\"9bb80d37\",\"largefrontend_flutter_chapter6_index.md\":\"5a321b81\",\"largefrontend_flutter_chapter5_material_scaffold.md\":\"8e6b896a\",\"largefrontend_flutter_chapter3_buttons.md\":\"ef950360\",\"largefrontend_flutter_chapter10_done_widget.md\":\"c3b44318\",\"largefrontend_flutter_chapter6_single_child_scrollview.md\":\"e3f75f3b\",\"largefrontend_flutter_chapter14_paint_flow.md\":\"d2f8102a\",\"largefrontend_flutter_chapter6_intro.md\":\"125a064e\",\"largefrontend_flutter_chapter5_transform.md\":\"22708e1e\",\"largefrontend_flutter_chapter14_layer.md\":\"4ed449fc\",\"largefrontend_flutter_chapter11_websocket.md\":\"0a022e47\",\"largefrontend_flutter_chapter5_fittedbox.md\":\"db915726\",\"largefrontend_flutter_chapter2_index.md\":\"d8e937b7\",\"largefrontend_flutter_chapter3_radio_and_checkbox.md\":\"025aba21\",\"largefrontend_flutter_chapter13_locallization_implement.md\":\"dc4b83fe\",\"largefrontend_flutter_chapter2_thread_model_and_error_report.md\":\"a29199d6\",\"largefrontend_flutter_chapter5_decoratedbox.md\":\"20f093ba\",\"largefrontend_flutter_chapter14_render_object.md\":\"55a1b551\",\"largefrontend_flutter_chapter14_element_buildcontext.md\":\"4272eae4\",\"largefrontend_flutter_chapter4_wrap_and_flow.md\":\"ac41b5bd\",\"largefrontend_flutter_chapter11_file_operation.md\":\"996a3a5d\",\"largefrontend_flutter_chapter13_faq.md\":\"ba7b9c70\",\"largefrontend_webfrontend_md_10.md\":\"51e0a312\",\"largefrontend_webfrontend_md_24.md\":\"0a4abd08\",\"largefrontend_flutter_chapter14_flutter_app_startup.md\":\"9b5942a1\",\"largefrontend_flutter_chapter10_custom_paint.md\":\"8b2a0a46\",\"largefrontend_flutter_chapter2_flutter_assets_mgr.md\":\"0e38b701\",\"largefrontend_interview_01.md\":\"2ac4b56d\",\"largefrontend_flutter_chapter8_index.md\":\"e83d3d80\",\"largefrontend_webfrontend_md_16.md\":\"a5610416\",\"largefrontend_flutter_chapter9_index.md\":\"3fb342a7\",\"largefrontend_flutter_chapter8_listener.md\":\"1b9b0316\",\"largefrontend_webfrontend_md_37.md\":\"3cacf3a2\",\"largefrontend_flutter_chapter4_layoutbuilder.md\":\"3d4bf94b\",\"largefrontend_webfrontend_md_17.md\":\"ee74edfb\",\"largefrontend_webfrontend_md_13.md\":\"b180bb5b\",\"largefrontend_flutter_chapter7_index.md\":\"0eb456b2\",\"largefrontend_webfrontend_md_31.md\":\"309453c7\",\"largefrontend_webfrontend_md_25.md\":\"95fdb4fe\",\"largefrontend_webfrontend_md_12.md\":\"3b14e491\",\"largefrontend_webfrontend_md_23.md\":\"18f08d72\",\"largefrontend_flutter_chapter11_json_model.md\":\"c84d2ccf\",\"largefrontend_webfrontend_md_01.md\":\"fbb7a35f\",\"largefrontend_flutter_chapter3_progress.md\":\"9bc148d3\",\"largefrontend_webfrontend_md_18.md\":\"ee651cc6\",\"largefrontend_flutter_sponsor.md\":\"e71431a9\",\"largefrontend_webfrontend_md_36.md\":\"f22f6d0d\",\"largefrontend_webfrontend_md_07.md\":\"71c2ad1d\",\"largefrontend_webfrontend_md_19.md\":\"e7907728\",\"largefrontend_webfrontend_md_20.md\":\"5a68bb4f\",\"largefrontend_webfrontend_md_32.md\":\"91d2f39f\",\"largefrontend_flutter_chapter7_futurebuilder_and_streambuilder.md\":\"af3535c0\",\"largefrontend_flutter_chapter9_hero.md\":\"25b46cf2\",\"largefrontend_flutter_chapter8_notification.md\":\"909f6756\",\"largefrontend_webfrontend_md_08.md\":\"0d59dce4\",\"largefrontend_flutter_chapter3_img_and_icon.md\":\"8b409b55\",\"largefrontend_webfrontend_md_04.md\":\"d6c15d55\",\"largefrontend_flutter_chapter9_animated_switcher.md\":\"9c0b3119\",\"largefrontend_webfrontend_md_22.md\":\"7d6fea68\",\"largefrontend_flutter_chapter2_flutter_widget_intro.md\":\"13414fea\",\"largefrontend_flutter_chapter13_intl.md\":\"d4776610\",\"largefrontend_overview_index.md\":\"3c87f69c\",\"largefrontend_flutter_chapter5_container.md\":\"373cc694\",\"largefrontend_wechatminiprogram_index.md\":\"7f40708d\",\"largefrontend_webfrontend_md_38.md\":\"3b6bb332\",\"linux_index.md\":\"624ecc66\",\"largefrontend_webfrontend_md_35.md\":\"f0538423\",\"myteam_index.md\":\"91a2c11f\",\"largefrontend_flutter_chapter6_pageview.md\":\"36fcbc65\",\"largefrontend_webfrontend_md_03.md\":\"2e5087f0\",\"largefrontend_webfrontend_md_21.md\":\"c181316e\",\"largefrontend_flutter_chapter3_input_and_form.md\":\"e4fba9dd\",\"largefrontend_flutter_chapter14_compositing.md\":\"6d96f932\",\"largefrontend_webfrontend_md_29.md\":\"c6fada7d\",\"largefrontend_flutter_chapter4_constraints.md\":\"9e4baace\",\"largefrontend_webfrontend_md_11.md\":\"63cd9c49\",\"largefrontend_webfrontend_md_14.md\":\"1423aeb1\",\"largefrontend_flutter_chapter6_tabview.md\":\"56048fb1\",\"largefrontend_webfrontend_md_33.md\":\"fdbe86fa\",\"largefrontend_flutter_chapter6_nestedscrollview.md\":\"2a66b86a\",\"largefrontend_webfrontend_md_34.md\":\"ad8ea220\",\"largefrontend_flutter_chapter2_flutter_router.md\":\"bb6e776c\",\"largefrontend_webfrontend_md_30.md\":\"cd2cb5aa\",\"largefrontend_flutter_chapter4_flex.md\":\"ebd43d6a\",\"largefrontend_webfrontend_md_09.md\":\"eedafcb1\",\"largefrontend_webfrontend_md_40.md\":\"4d4fefa1\",\"largefrontend_flutter_join_us.md\":\"44c81d86\",\"largefrontend_flutter_chapter9_route_transition.md\":\"f56cf837\",\"largefrontend_uniapp_index.md\":\"fa387c39\",\"largefrontend_flutter_chapter4_row_and_column.md\":\"964229f5\",\"largefrontend_flutter_chapter9_stagger_animation.md\":\"315538f5\",\"largefrontend_flutter_chapter3_text.md\":\"6720230c\",\"largefrontend_flutter_chapter10_watermark.md\":\"2e4ddf03\",\"largefrontend_flutter_chapter11_http.md\":\"efd2ee64\",\"largefrontend_webfrontend_md_06.md\":\"e20b049a\",\"largefrontend_flutter_summary.md\":\"2e31b0a8\",\"largefrontend_flutter_chapter6_animatedlist.md\":\"20d9436f\",\"largefrontend_flutter_chapter5_clip.md\":\"91f4174c\",\"largefrontend_webfrontend_md_02.md\":\"40cd2099\",\"largefrontend_flutter_chapter6_gridview.md\":\"b3be7e60\",\"largefrontend_flutter_chapter6_custom_scrollview.md\":\"01be950c\",\"largefrontend_flutter_chapter11_download_with_chunks.md\":\"9379cc89\",\"largefrontend_flutter_chapter6_scroll_controller.md\":\"ffa2d5fd\",\"largefrontend_flutter_chapter9_intro.md\":\"3fc29b5f\",\"largefrontend_flutter_chapter1_dart.md\":\"6bba4e7c\",\"largefrontend_flutter_chapter7_inherited_widget.md\":\"5a21d704\",\"largefrontend_flutter_index.md\":\"cd81fbbe\",\"largefrontend_flutter_reference.md\":\"36336375\",\"largefrontend_flutter_chapter6_keepalive.md\":\"535137be\",\"largefrontend_flutter_chapter14_layout.md\":\"0d7a8df3\",\"largefrontend_flutter_chapter6_sliver.md\":\"0c8b7553\",\"largefrontend_webfrontend_md_39.md\":\"aef49a57\",\"largefrontend_webfrontend_md_15.md\":\"bf9ff1a1\",\"largefrontend_flutter_chapter14_image_and_cache.md\":\"9622c56b\",\"largefrontend_webfrontend_md_28.md\":\"83169d10\",\"largefrontend_flutter_chapter8_eventbus.md\":\"fc0db86d\",\"largefrontend_flutter_chapter7_value_listenable_builder.md\":\"a0c76121\",\"largefrontend_flutter_preface.md\":\"604b9934\",\"largefrontend_flutter_chapter7_theme.md\":\"c21036bc\",\"largefrontend_webfrontend_md_05.md\":\"f7607009\",\"largefrontend_flutter_chapter6_listview.md\":\"472910bd\",\"largefrontend_flutter_chapter7_provider.md\":\"ab590744\",\"largefrontend_flutter_chapter8_gesture.md\":\"859bbb38\",\"largefrontend_flutter_chapter9_animated_widgets.md\":\"e8cd3c4d\",\"largefrontend_webfrontend_md_26.md\":\"4928006b\",\"largefrontend_flutter_chapter10_gradient_circular_progress_demo.md\":\"b3d48a2c\",\"largefrontend_flutter_chapter8_hittest.md\":\"e963812b\",\"largefrontend_flutter_chapter7_willpopscope.md\":\"d224ecce\",\"largefrontend_flutter_chapter9_animation_structure.md\":\"672eb3c0\",\"largefrontend_flutter_chapter8_gesture_conflict.md\":\"15bbadba\",\"largefrontend_webfrontend_md_27.md\":\"0b5b5e39\",\"largefrontend_flutter_chapter7_dailog.md\":\"5f91e51a\",\"largefrontend_flutter_chapter10_custom_checkbox.md\":\"0db7d50f\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"zh-CN\",\"dir\":\"ltr\",\"title\":\"落光的Pro博客\",\"description\":\"Vite & Vue powered static site generator.\",\"base\":\"./\",\"head\":[],\"appearance\":true,\"themeConfig\":{\"search\":{\"provider\":\"local\",\"options\":{\"locales\":{\"zh\":{\"translations\":{\"button\":{\"buttonText\":\"搜索文档\",\"buttonAriaLabel\":\"搜索文档\"},\"modal\":{\"noResultsText\":\"无法找到相关结果\",\"resetButtonTitle\":\"清除查询条件\",\"footer\":{\"selectText\":\"选择\",\"navigateText\":\"切换\"}}}}}}},\"logo\":\"/icon-radius.png\",\"nav\":[{\"text\":\"大前端\",\"items\":[{\"text\":\"Web前端\",\"link\":\"/largeFrontEnd/webFrontEnd/md/01.md\"},{\"text\":\"Uni-app\",\"link\":\"/largeFrontEnd/uniapp/\"},{\"text\":\"微信小程序\",\"link\":\"/largeFrontEnd/weChatMiniProgram/\"},{\"text\":\"Electron\",\"link\":\"/largeFrontEnd/electron/\"},{\"text\":\"Flutter\",\"link\":\"/largeFrontEnd/flutter/\"},{\"text\":\"前端面试题\",\"link\":\"/largeFrontEnd/interview/01.md\"}]},{\"text\":\"后端\",\"link\":\"/backEnd/\"},{\"text\":\"Linux\",\"link\":\"/linux/\"},{\"text\":\"我的团队\",\"link\":\"/myTeam/\"}],\"sidebar\":{\"/largeFrontEnd/webFrontEnd/\":[{\"text\":\"第一阶段  基础入门\",\"collapsed\":true,\"items\":[{\"text\":\"一、基础认知\",\"link\":\"/largeFrontEnd/webFrontEnd/md/01.md\"},{\"text\":\"二、HTML标签学习\",\"link\":\"/largeFrontEnd/webFrontEnd/md/02.md\"},{\"text\":\"三、HTML基础\",\"link\":\"/largeFrontEnd/webFrontEnd/md/03.md\"},{\"text\":\"四、CSS基础\",\"link\":\"/largeFrontEnd/webFrontEnd/md/04.md\"},{\"text\":\"五、CSS进阶\",\"link\":\"/largeFrontEnd/webFrontEnd/md/05.md\"},{\"text\":\"六、盒子模型\",\"link\":\"/largeFrontEnd/webFrontEnd/md/06.md\"},{\"text\":\"七、CSS浮动\",\"link\":\"/largeFrontEnd/webFrontEnd/md/07.md\"},{\"text\":\"八、CSS定位装饰\",\"link\":\"/largeFrontEnd/webFrontEnd/md/08.md\"},{\"text\":\"九、CSS精灵图\",\"link\":\"/largeFrontEnd/webFrontEnd/md/09.md\"},{\"text\":\"项目前置认知\",\"link\":\"/largeFrontEnd/webFrontEnd/md/10.md\"},{\"text\":\"项目结构搭建\",\"link\":\"/largeFrontEnd/webFrontEnd/md/11.md\"},{\"text\":\"字体图标、动画\",\"link\":\"/largeFrontEnd/webFrontEnd/md/12.md\"},{\"text\":\"移动web开发\",\"link\":\"/largeFrontEnd/webFrontEnd/md/13.md\"}]},{\"text\":\"第二阶段 技术进阶\",\"collapsed\":true,\"items\":[{\"text\":\"JavaScript深入浅出\",\"link\":\"/largeFrontEnd/webFrontEnd/md/14.md\"},{\"text\":\"JavaScript核心之web APIs\",\"link\":\"/largeFrontEnd/webFrontEnd/md/15.md\"},{\"text\":\"一、Web APIS导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/16.md\"},{\"text\":\"二、DOM导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/17.md\"},{\"text\":\"三、事件高级导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/18.md\"},{\"text\":\"四、BOM导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/19.md\"},{\"text\":\"五、PC网页特效导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/20.md\"},{\"text\":\"六、移动端特效导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/21.md\"},{\"text\":\"七、本地存储导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/22.md\"},{\"text\":\"jQuery入门导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/23.md\"},{\"text\":\"数据可视化项目导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/24.md\"}]},{\"text\":\"Node.js\",\"collapsed\":true,\"items\":[{\"text\":\"buffer(缓冲器)\",\"link\":\"/largeFrontEnd/webFrontEnd/md/25.md\"},{\"text\":\"计算机基础\",\"link\":\"/largeFrontEnd/webFrontEnd/md/26.md\"},{\"text\":\"fs模块\",\"link\":\"/largeFrontEnd/webFrontEnd/md/27.md\"},{\"text\":\"path模块\",\"link\":\"/largeFrontEnd/webFrontEnd/md/28.md\"},{\"text\":\"HTTP模块\",\"link\":\"/largeFrontEnd/webFrontEnd/md/29.md\"},{\"text\":\"Express框架\",\"link\":\"/largeFrontEnd/webFrontEnd/md/30.md\"},{\"text\":\"Mongodb\",\"link\":\"/largeFrontEnd/webFrontEnd/md/31.md\"}]},{\"text\":\"Ajax\",\"link\":\"/largeFrontEnd/webFrontEnd/md/32.md\"},{\"text\":\"前端面试题\",\"collapsed\":true,\"items\":[{\"text\":\"一、CSS\",\"link\":\"/largeFrontEnd/webFrontEnd/md/33.md\"},{\"text\":\"二、JavaScript\",\"link\":\"/largeFrontEnd/webFrontEnd/md/34.md\"},{\"text\":\"三、HTML5CSS3\",\"link\":\"/largeFrontEnd/webFrontEnd/md/35.md\"},{\"text\":\"四、Vue\",\"link\":\"/largeFrontEnd/webFrontEnd/md/36.md\"},{\"text\":\"五、Echarts\",\"link\":\"/largeFrontEnd/webFrontEnd/md/37.md\"},{\"text\":\"六.Uni-APP\",\"link\":\"/largeFrontEnd/webFrontEnd/md/38.md\"},{\"text\":\"七、webpack\",\"link\":\"/largeFrontEnd/webFrontEnd/md/39.md\"},{\"text\":\"八、Git\",\"link\":\"/largeFrontEnd/webFrontEnd/md/40.md\"}]}],\"/largeFrontEnd/flutter/\":[{\"text\":\"Flutter入门到实战\",\"collapsed\":false,\"items\":[{\"text\":\"第一章 初识Flutter\",\"link\":\"/largeFrontEnd/flutter/chapter1/index.md\"},{\"text\":\"第二章 简介\",\"link\":\"/largeFrontEnd/flutter/chapter2/index.md\"},{\"text\":\"第三章 基础组件\",\"link\":\"/largeFrontEnd/flutter/chapter3/index.md\"},{\"text\":\"第四章 布局类组件\",\"link\":\"/largeFrontEnd/flutter/chapter4/index.md\"},{\"text\":\"第五章 容器类Widget\",\"link\":\"/largeFrontEnd/flutter/chapter5/index.md\"},{\"text\":\"第六章 可滚动组件\",\"link\":\"/largeFrontEnd/flutter/chapter6/index.md\"},{\"text\":\"第七章 功能型Widget简介\",\"link\":\"/largeFrontEnd/flutter/chapter7/index.md\"},{\"text\":\"第八章 事件处理与通知\",\"link\":\"/largeFrontEnd/flutter/chapter8/index.md\"},{\"text\":\"第九章 动画\",\"link\":\"/largeFrontEnd/flutter/chapter9/index.md\"},{\"text\":\"第十章 自定义组件\",\"link\":\"/largeFrontEnd/flutter/chapter10/index.md\"},{\"text\":\"第十一章 文件操作\",\"link\":\"/largeFrontEnd/flutter/chapter11/index.md\"},{\"text\":\"第十二章 Flutter 扩展\",\"link\":\"/largeFrontEnd/flutter/chapter12/index.md\"},{\"text\":\"第十三章 多语言\",\"link\":\"/largeFrontEnd/flutter/chapter13/index.md\"},{\"text\":\"第十四章 高级进阶\",\"link\":\"/largeFrontEnd/flutter/chapter14/index.md\"}]}]},\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://gitee.com/luoguangguang\"},{\"icon\":{\"svg\":\"<svg role=\\\"img\\\" viewBox=\\\"0 0 24 24\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\"><title>Dribbble</title><path d=\\\"M12...6.38z\\\"/></svg>\"},\"link\":\"...\",\"ariaLabel\":\"cool link\"}],\"footer\":{\"message\":\"Released under the MIT License.\",\"copyright\":\"Copyright © 2023-present LG of GZU\"},\"editLink\":{\"pattern\":\"https://gitee.com/luoguangguang/note/tree/master/docs/:path\",\"text\":\"在Gitee上参与编辑此页\"},\"carbonAds\":{\"code\":\"your-carbon-code\",\"placement\":\"your-carbon-placement\"}},\"locales\":{},\"scrollOffset\":90,\"cleanUrls\":false}");</script>
    
  </body>
</html>